From d97054eb5fa9db01ed3cb9a3290fc0090c7c3812 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 26 Jun 2026 14:32:19 +0000 Subject: [PATCH] ci+docs(wrappers): detect and fix variable metadata drift (closes #18) The wrapper-forwarding job only checked that wrappers expose the same variable NAMES as their core modules; descriptions/types/defaults could silently diverge, shipping truncated or wrong docs (and even a factual error: schema_version_ endpoint_path said "ASM API" when it applies to SAT too). Fix the drift: sync every wrapper variable's description to its core module's verbatim across all 12 wrappers (430 descriptions updated, including the 8 asm-aws-ha mismatches and the schema_version_endpoint_path correction). After this, descriptions, types, and defaults all match core. Add detection: a new, dependency-free .github/scripts/wrapper_drift_check.py (brace-aware parser, handles validation blocks) compares each forwarded variable's description/type/default against core and emits ::warning:: annotations. Wired into the wrapper-forwarding job as a NON-BLOCKING step so future drift surfaces in the PR without breaking the build. Reports 0 warnings on the current tree. terraform fmt, validate, and tflint pass on the wrappers. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01AFfgnPaoyGhp52vQL2PVU3 --- .../wrapper_drift_check.cpython-311.pyc | Bin 0 -> 5988 bytes .github/scripts/wrapper_drift_check.py | 136 +++++++++++++++ .github/workflows/ci.yml | 6 + modules/asm-aws-autoscale/variables.tf | 163 +++++++++++------- modules/asm-aws-ha/variables.tf | 34 ++-- modules/asm-aws-single/variables.tf | 2 +- modules/asm-azure-autoscale/variables.tf | 85 +++++---- modules/asm-azure-ha/variables.tf | 52 +++--- modules/asm-azure-single/variables.tf | 10 +- modules/sat-aws-autoscale/variables.tf | 163 +++++++++++------- modules/sat-aws-ha/variables.tf | 34 ++-- modules/sat-aws-single/variables.tf | 2 +- modules/sat-azure-autoscale/variables.tf | 85 +++++---- modules/sat-azure-ha/variables.tf | 52 +++--- modules/sat-azure-single/variables.tf | 10 +- 15 files changed, 528 insertions(+), 306 deletions(-) create mode 100644 .github/scripts/__pycache__/wrapper_drift_check.cpython-311.pyc create mode 100755 .github/scripts/wrapper_drift_check.py diff --git a/.github/scripts/__pycache__/wrapper_drift_check.cpython-311.pyc b/.github/scripts/__pycache__/wrapper_drift_check.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f7742eae8c7d4bf379528ee8bd386f4c11ee179a GIT binary patch literal 5988 zcma)AU2GfImA=E7;m?pG>W8+(*!AA{Poiu}wj0;BVtXswvg|fhvbLKhs#9vj8Hpp4 zGwhwAewbmwF1o--AYeMk!fJj{W|5>W(uX`0i?ry&KJ0TL21H8?V8Ce6hrY2$iyD5~ zbB8}79b=oB;o;nK&pr3tpYxqF_vfN0Ah;$=7gzKFg#JVe?MrOEeDUA#av#ZP9?6XC zNi*||`|p|e$Sm9(+&tV~xP5T@;SRhPnrCGp#msXaq%bRdTd5rUm_g_h_%*M2uN?Xb zn)k^fP`}&CfYXC=dkn<=8_mQx0>^_(U{je?C7$iS6oG|=_CmXB+!aMmy8ucbb=Q*1gd_a zHOe@MtwF7-Eid2|eKB?%FXl|wg7Nln$FaTy9NkcMyOm5;*9`o};@iT4tQbk6W+`Xk zI9@O}vI_l|l_e>cHW#Q6PD&a!)U=|R=?yHaVDhpufv1}@31)Wb5R8R2Y zFJJ!l^|{$8*dAEP&^uuC1S!#DOp966)mYk?{pm{Y-C`R&?6WW zmtZIg>|tVnzocLQX%Jgz@4^LPF|8->(7BdO*r1vzf$5rJz@k#Av`Hz%|!1A0Td^-vnxt=m;Pv@6nKI=(CcEJ~XBXnQGc8^;cgeg6js-#8XK zc-t7i{btg|%@#a*;h%-y7rzApIL;J2W(R)}vKW)rwl3=_Ed3ZXebUi(1-&s*U@c^^ z(RKq=;4Dt|w2<0sK}^U)olP#E(2^N_rS{6O|_aKTB?+ z7@xm*Lossz<|=I4b-Y5c5-_%jXc@^&0_ZNo#;0KB?qLkT;W~x8_a+D($rI+yztHhN zVG1#i!xKqcRvZ?~^u}MhqOyll*i9g{yMO5F8?ZxeBFj_h+I4L+5bBNV1;{Q6kX;lYzbJT@QNd>h zc4aNr40e_N+0I=n_+jVzyLN8C@>S@_zjRrF-Vrg#;>ob(B`mc=8?XYhx3z~^*#{gy zaQrQfhuT{@+r^Du4rLwmtm-<4h(Q`tT9?I&|5?&<-?RCauXoIqu9@D|)7w{X3-Hiz{)?G)6QYL+vBcD~@igQw zX}q9G83kX!=iQilaiN)8NxQk1>mYbhmp33vQRliuBhVyu!8-~?X&Q+jTxKBUP~&M; zgD?l-e6pPav|>38vaJZT8-QGW&DaAehBoR3tEC)3glbOUCZIGk1|qFBvjRvu)=fON zmV)qOWN9qaAOV4pW+-yZ)d#RLr)$cDTgdyz{xuW(0YGow2jl|$=3J<``h>ZC>j`r( z&%XKnF+#Umo-Mo)d%_4$80?^!;CP7Qcnw7&$&}-TOiRiF7Ez#}Ye(o3I9{5tYO=#- z^{m6K=&I)MMmDXQ(43=F=5Ta+9VP>O69PM-nX=N0g>dn5T?iE~Q@A_*{-JI8 zr?K~9Rq=35JlsG-{Gdy^;m#ktD{Kknlbd2mw8eUG001$tryd4LUi_E(-hE}Z%+?16 z%ko>%av(nuf8aJLC1+ae|0_+-EXJ1Eaj0$1cq`R?oRk~WVc{DNTb#Dm%WeEOkrSmbj zD+_?n_DVxqz&smVWe#+F1t9Ake}T6ci*Ke2CY{dKMY+9_AOrjRKRjJP?=^?o@-Qy1 z{8WplYQ4Sso~rfr*2@0(Sj%H^=b?krJqtcOV}ZFmpEH-@XJd@xhwNIX-(Rlx6FPNH zua9*D!cDG$N$ zZqNnl!3V+p5J*7IX^=Bexj?1<0i=i;9+n?`7#=ELshrsfAFGCs)xyXA5I*sF_(U~) zsun(Fb2bMV$bO0v;Js~94-eP@dN){LJ%0T7Q4=4A?k2Vp+qbIX!J2rmf&9GS62OLV z|J}8%wek-)^QFAa)%zp&g|~$5uUD9#`~J)KLEytcwST3i2d5+1zp z>7FV+Q^RLId%lWa+=;wc3!k^SKkvhpNTpESce=Lk^utJW`*6iujf~bJqYsDoZ{Mn% zs}9F&!|{fX+XLMLv03}dCN)+-p>XkA21VI_KXep*96HVZiaE_Dq1?F!;m!5$-@E<| zNjEY(l?Z(mZw7z+^=(L>3N&Hw@;TXKG4F!Ez5lewu}?{Po+-tdUA|{=U&WGM&j!2F zu3eA{S-jbmWUL~Cvv`YLWe8ImvRI4n@LCZNv!o1x{*pIJRzV32w?HxLYh@1jM zv=!aJ1Pd1UM^)yzv-5!tT zJ)=0k4^C5-mgrH4dm7bs5@k&0eN*na(1m=UeIRuqf1q=|)pMHFJk6NG`4DcMPmCF{ ziI~^%fA7ZhE3eGlxCwA!Dw(EhLq?(#`0#eT*REf=GBfA!SpugZv4G=mwKg1It0RY_ zMme6fRmYQDb#cVaF&t*i;g?h;EgLk8X+a6b&18eiz+nNIq4NVjLkA}*BJl3wwTIE? ze|F%V1J!7(7L7GfAHUZno1*P2M;?m9J0s@+WJb<4pQ<=j6Q^vCTk(y`3p*p{PNmo%x6b_>;L@tH+PPI zvmV)BVeWmWd=0>);qgRoFrSV#5dCk``jJ?J?JCAkbr&BU8g1~qwU17m{Pf7L{5`Ch ztN)Yx-`BcXe`UEnd;bRD1oi=W-0)FT12D9|k04&y5vCuU1=4(~+@%_KX@|Q68IgU` zO-USoB0*Ook%)+wuv0;#Nbb)AOb9_lXlZBm2PEuAUoVby|T}P^l zrlE<1!_fn-Um&*!ce$VV&o?*xMe-eJPBR(f5fFfVhG}s94A(%E97c>#4A8rdgra*l zylD7neeaR_;6eICC+IU0qfg{0efFJ#rx6uHo(2L^^fxdHM(lk>@1wwwJy?2fCvu`1 zI9UsvEP5N>AhQQnzD@QYV#XV&LtbLSOt^tMJqJ?m4E!H)J`)uH literal 0 HcmV?d00001 diff --git a/.github/scripts/wrapper_drift_check.py b/.github/scripts/wrapper_drift_check.py new file mode 100755 index 0000000..d4e4077 --- /dev/null +++ b/.github/scripts/wrapper_drift_check.py @@ -0,0 +1,136 @@ +#!/usr/bin/env python3 +"""Warn when a wrapper module's variable metadata drifts from its core module. + +The wrapper modules (asm-*, sat-*) are thin pass-throughs around the shared tier +modules. They must re-declare every core variable (enforced as a hard error by +the `wrapper-forwarding` job), but the *metadata* of those re-declarations — +`description`, `type`, `default` — can silently diverge. A drifted description +ships truncated/incorrect docs to customers via terraform-docs; a drifted +default silently overrides the core; a drifted type can accept values the core +rejects. + +This check is intentionally NON-BLOCKING: it emits GitHub `::warning::` +annotations and always exits 0, so it surfaces drift in the PR without breaking +the build. Tighten to errors once the tree is clean if desired. + +No third-party dependencies — a small brace-aware parser handles `variable` +blocks that contain nested `validation { ... }` blocks. +""" + +import re +import sys + +WRAPPERS = { + "asm-aws-single": "single-vm/aws", + "sat-aws-single": "single-vm/aws", + "asm-aws-ha": "ha-hot-hot/aws", + "sat-aws-ha": "ha-hot-hot/aws", + "asm-aws-autoscale": "unlimited-scale/aws", + "sat-aws-autoscale": "unlimited-scale/aws", + "asm-azure-single": "single-vm/azure", + "sat-azure-single": "single-vm/azure", + "asm-azure-ha": "ha-hot-hot/azure", + "sat-azure-ha": "ha-hot-hot/azure", + "asm-azure-autoscale": "unlimited-scale/azure", + "sat-azure-autoscale": "unlimited-scale/azure", +} + +# Variables a wrapper deliberately does not forward. +HIDDEN = {"product"} + +VAR_RE = re.compile(r'variable\s+"([^"]+)"\s*\{') + + +def _block_end(text, open_brace_idx): + """Return index just past the matching close brace for the block.""" + depth = 0 + for i in range(open_brace_idx, len(text)): + c = text[i] + if c == "{": + depth += 1 + elif c == "}": + depth -= 1 + if depth == 0: + return i + 1 + return len(text) + + +def _attr(body, name): + """Extract a top-level `name = ` from a variable block body. + + Brace/bracket-aware so multi-line type/default values and nested validation + blocks don't confuse it. Returns the raw value text (whitespace-normalised) + or None. + """ + m = re.search(r'(?m)^\s*' + re.escape(name) + r'\s*=\s*', body) + if not m: + return None + start = m.end() + depth = 0 + in_str = False + esc = False + out = [] + for i in range(start, len(body)): + c = body[i] + out.append(c) + if in_str: + if esc: + esc = False + elif c == "\\": + esc = True + elif c == '"': + in_str = False + continue + if c == '"': + in_str = True + elif c in "{[(": + depth += 1 + elif c in "}])": + depth -= 1 + elif c == "\n" and depth == 0: + out.pop() + break + return " ".join("".join(out).split()) + + +def parse_vars(path): + try: + text = open(path, encoding="utf-8").read() + except FileNotFoundError: + return {} + out = {} + for m in VAR_RE.finditer(text): + name = m.group(1) + brace = text.index("{", m.start()) + body = text[brace + 1 : _block_end(text, brace) - 1] + out[name] = { + "description": _attr(body, "description"), + "type": _attr(body, "type"), + "default": _attr(body, "default"), + } + return out + + +def main(): + warnings = 0 + for wrapper, core in WRAPPERS.items(): + wv = parse_vars(f"modules/{wrapper}/variables.tf") + cv = parse_vars(f"modules/{core}/variables.tf") + for name, c in sorted(cv.items()): + if name in HIDDEN or name not in wv: + continue # missing vars are caught by the forwarding name check + w = wv[name] + for field in ("description", "type", "default"): + if c[field] is not None and w[field] != c[field]: + warnings += 1 + print( + f"::warning file=modules/{wrapper}/variables.tf::" + f"variable '{name}' {field} differs from core " + f"modules/{core}: core={c[field]!r} wrapper={w[field]!r}" + ) + print(f"wrapper metadata drift check: {warnings} warning(s).") + return 0 # non-blocking + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9c869ed..20a1c9b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -429,6 +429,12 @@ jobs: fi echo "All wrapper modules forward their core surface area cleanly." + # Non-blocking: warn (don't fail) when a forwarded variable's + # description/type/default has drifted from the core module, so the + # public-API docs stay faithful to the implementation. + - name: Warn on wrapper/core variable metadata drift + run: python3 .github/scripts/wrapper_drift_check.py + # --------------------------------------------------------------------- # versions.tf existence + pinning. Every module dir that has a *.tf # file should also have a versions.tf declaring required_providers diff --git a/modules/asm-aws-autoscale/variables.tf b/modules/asm-aws-autoscale/variables.tf index a8c566b..b8bd362 100644 --- a/modules/asm-aws-autoscale/variables.tf +++ b/modules/asm-aws-autoscale/variables.tf @@ -3,11 +3,13 @@ # which is hardcoded by this wrapper). variable "vpc_id" { - type = string + description = "VPC to deploy into." + type = string } variable "public_subnet_ids" { - type = list(string) + description = "At least two public subnet IDs in different AZs for the ALB." + type = list(string) validation { condition = length(var.public_subnet_ids) >= 2 error_message = "At least two public subnets are required." @@ -15,7 +17,8 @@ variable "public_subnet_ids" { } variable "private_subnet_ids" { - type = list(string) + description = "At least three private subnet IDs, one per AZ, for ASG instances, RDS, and ElastiCache." + type = list(string) validation { condition = length(var.private_subnet_ids) >= 3 error_message = "At least three private subnets (one per AZ) are required for unlimited-scale." @@ -23,11 +26,13 @@ variable "private_subnet_ids" { } variable "allowed_cidrs" { - type = list(string) + description = "CIDR blocks permitted to reach the ALB on port 443 (and port 80 when enable_http_redirect is true)." + type = list(string) } variable "acm_certificate_arn" { - type = string + description = "ARN of an ACM certificate in the same region for the ALB HTTPS listener." + type = string } variable "alert_email" { @@ -37,98 +42,117 @@ variable "alert_email" { } variable "asg_min_size" { - type = number - default = 3 + description = "Minimum number of instances the ASG maintains." + type = number + default = 3 } variable "asg_max_size" { - type = number - default = 20 + description = "Maximum number of instances the ASG can scale out to." + type = number + default = 20 } variable "asg_desired_capacity" { - type = number - default = 3 + description = "Starting instance count when the ASG is created. Must be between asg_min_size and asg_max_size." + type = number + default = 3 } variable "instance_type" { - type = string - default = "m6i.large" + description = "EC2 instance type for the ASG launch template. m6i.large is a balanced starting point; scale to m6i.xlarge for larger tenants." + type = string + default = "m6i.large" } variable "target_cpu_utilization" { - type = number - default = 60 + description = "Target average CPU utilization (percent, 1-100) for the ASG target-tracking scale-out policy." + type = number + default = 60 } variable "target_request_count_per_target" { - type = number - default = 500 + description = "Target ALB request count per instance for the request-count scale-out policy." + type = number + default = 500 } variable "db_instance_class" { - type = string - default = "db.r6g.large" + description = "RDS instance class. db.r6g.large handles up to ~200 concurrent connections; scale to r6g.xlarge for larger deployments." + type = string + default = "db.r6g.large" } variable "db_allocated_storage_gb" { - type = number - default = 200 + description = "Initial allocated storage for the RDS instance in GiB." + type = number + default = 200 } variable "db_max_allocated_storage_gb" { - type = number - default = 2000 + description = "Upper limit for RDS storage autoscaling in GiB. Must be >= db_allocated_storage_gb." + type = number + default = 2000 } variable "db_engine_version" { - type = string - default = "16.4" + description = "PostgreSQL engine version. Pin to a specific minor version (e.g. 16.4) to prevent unexpected minor upgrades during terraform apply." + type = string + default = "16.4" } variable "db_backup_retention_days" { - type = number - default = 30 + description = "Days RDS retains automated daily backups. 30 covers a typical monthly review cycle and aligns with the pre-patch on-demand snapshot lifecycle." + type = number + default = 30 } variable "db_read_replica_count" { - type = number - default = 2 + description = "Number of RDS read replicas to create. Replicas reduce read load and provide manual failover targets." + type = number + default = 2 } variable "db_deletion_protection" { - type = bool - default = true + description = "Enable RDS deletion protection. Default true; set false only in dev/test sandboxes where terraform destroy should succeed." + type = bool + default = true } variable "environment" { - type = string - default = "prod" + description = "Short environment label (e.g. prod, staging) appended to resource names and tags." + type = string + default = "prod" } variable "name_prefix" { - type = string - default = null + description = "Optional prefix prepended to all resource names. Defaults to '-unlimited-scale' when null." + type = string + default = null } variable "enable_customer_managed_key" { - type = bool - default = true + description = "Create a KMS customer-managed key for encrypting RDS, ElastiCache, and S3 backup objects. Disable only if your organization manages keys externally." + type = bool + default = true } variable "alb_min_tls_version" { - type = string - default = "ELBSecurityPolicy-TLS13-1-2-2021-06" + description = "ELBSecurityPolicy name defining the minimum TLS version and cipher suite for the ALB HTTPS listener." + type = string + default = "ELBSecurityPolicy-TLS13-1-2-2021-06" } variable "enable_flow_logs" { - type = bool - default = true + description = "Publish VPC Flow Logs to CloudWatch. Recommended for production compliance." + type = bool + default = true } variable "access_log_retention_days" { - type = number - default = 90 + description = "CloudWatch log retention in days for ALB access logs and VPC Flow Logs." + type = number + default = 90 } variable "marketplace_product_code" { @@ -146,49 +170,49 @@ variable "enable_http_redirect" { # ----- Patching and migration safety ----- variable "create_backup_bucket" { - description = "Provision an S3 bucket with versioning + object-lock + lifecycle for pre-patch /api/instance/export bundles." + description = "Provision an S3 bucket (versioning + object-lock governance + lifecycle to IA at 30d and Deep Archive at 90d) for pre-patch /api/instance/export bundles. The SAT instance profile gets least-privilege PutObject on hailbytes-*.tar.gz." type = bool default = true } variable "backup_bucket_name" { - description = "Name of an existing S3 bucket to use for pre-patch backups." + description = "Name of an existing S3 bucket to use for pre-patch backups. If null and create_backup_bucket is true, the module names one '-backups-'. If non-null and create_backup_bucket is false, the module only attaches the IAM PutObject policy to the existing bucket." type = string default = null } variable "backup_object_lock_retention_days" { - description = "Object Lock (governance mode) retention period for backup objects." + description = "Object Lock (governance mode) retention period for backup objects. 30 days satisfies the procurement-grade safety net while still allowing privileged operator override for compaction." type = number default = 30 } variable "backup_noncurrent_version_expiration_days" { - description = "Expire noncurrent versions of backup objects after this many days." + description = "Expire noncurrent versions of backup objects after this many days. 365 retains a year of pre-patch bundles for rollback." type = number default = 365 } variable "instance_refresh_min_healthy_percentage" { - description = "Minimum percentage of the ASG that must remain healthy during an instance refresh." + description = "Minimum percentage of the ASG that must remain healthy during an instance refresh. 50 drains one instance at a time on a 2-instance ASG; tune higher for larger fleets that can tolerate parallel replacement." type = number default = 50 } variable "instance_refresh_instance_warmup_seconds" { - description = "Seconds the ASG considers a new instance 'warming up' before counting toward healthy_percentage." + description = "Seconds the ASG considers a new instance 'warming up' before counting toward healthy_percentage. 120 is enough for the SAT marketplace AMI to pass the ALB /health probe; raise for slower boot images." type = number default = 120 } variable "refresh_rollback_5xx_threshold_pct" { - description = "Target-group 5xx rate (percent) that triggers instance-refresh auto-rollback." + description = "Target-group 5xx rate (percent) above which the instance refresh auto-rollback alarm fires. Default 1% over 2 evaluation periods of 1 minute." type = number default = 1 } variable "waf_web_acl_arn" { - description = "Optional ARN of an existing WAFv2 web ACL to associate with the ALB." + description = "Optional ARN of an existing WAFv2 web ACL to associate with the ALB. Defaults to null (not attached). HailBytes does not bundle a managed ruleset; most enterprises bring their own." type = string default = null } @@ -200,7 +224,7 @@ variable "rds_copy_tags_to_snapshot" { } variable "schema_version_endpoint_path" { - description = "Path on the ASM API that returns the running schema version." + description = "Path on the SAT/ASM API that returns the running schema version. Used by the schema_version_endpoint output that customer CI/CD curls in post-patch verification." type = string default = "/api/instance/schema-version" } @@ -220,13 +244,15 @@ variable "redis_node_type" { } variable "redis_engine_version" { - type = string - default = "7.1" + description = "Redis engine version for the ElastiCache replication group." + type = string + default = "7.1" } variable "redis_snapshot_retention_days" { - type = number - default = 0 + description = "Days ElastiCache retains automatic snapshots. 0 disables snapshots." + type = number + default = 0 } variable "redis_endpoint_override" { @@ -236,18 +262,20 @@ variable "redis_endpoint_override" { } variable "redis_endpoint_override_port" { - type = number - default = 6379 + description = "TCP port for the customer-managed Redis endpoint supplied via redis_endpoint_override." + type = number + default = 6379 } variable "redis_endpoint_override_tls" { - type = bool - default = true + description = "Whether to connect to the customer-managed Redis endpoint over TLS." + type = bool + default = true } variable "enable_alb_deletion_protection" { - description = "Enable deletion protection on the ALB. Default true." + description = "Enable deletion protection on the ALB. Default true; production deployments should keep this on. Set to false in dev/test sandboxes where `terraform destroy` should succeed without manual cleanup." type = bool default = true } @@ -256,19 +284,19 @@ variable "enable_alb_deletion_protection" { # ----- RDS production-hardening (opt-in) ----- variable "rds_enhanced_monitoring_interval" { - description = "RDS enhanced monitoring sample interval. 0 disables. CKV_AWS_118." + description = "RDS enhanced monitoring sample interval in seconds. 0 disables. Default 0; production typically 60. CKV_AWS_118." type = number default = 0 } variable "rds_enabled_cloudwatch_log_types" { - description = "RDS log types to export to CloudWatch. CKV_AWS_129." + description = "RDS log types to export to CloudWatch. Empty list = none (cost-saving default). Production should set [\"postgresql\", \"upgrade\"]. CKV_AWS_129." type = list(string) default = [] } variable "rds_iam_authentication_enabled" { - description = "Enable IAM DB authentication. CKV_AWS_161." + description = "Enable IAM database authentication on RDS. CKV_AWS_161." type = bool default = false } @@ -280,12 +308,13 @@ variable "rds_performance_insights_enabled" { } variable "rds_performance_insights_retention_days" { - description = "Performance Insights retention. 7 = free tier; 731 = long-term." + description = "Performance Insights data retention. 7 = free tier (default); 731 = long-term." type = number default = 7 } variable "tags" { - type = map(string) - default = {} + description = "Additional tags merged on to every taggable resource." + type = map(string) + default = {} } diff --git a/modules/asm-aws-ha/variables.tf b/modules/asm-aws-ha/variables.tf index 4d719a1..ec42ea9 100644 --- a/modules/asm-aws-ha/variables.tf +++ b/modules/asm-aws-ha/variables.tf @@ -83,7 +83,7 @@ variable "db_engine_version" { } variable "db_backup_retention_days" { - description = "Deprecated alias for rds_backup_retention_period. Kept for backward compatibility." + description = "Deprecated alias for rds_backup_retention_period. When set (non-null), this value wins over rds_backup_retention_period. Leave null and use rds_backup_retention_period instead. Kept for backward compatibility with pre-patching-safety configs." type = number default = null } @@ -130,7 +130,7 @@ variable "enable_http_redirect" { # ----- Patching and migration safety ----- variable "db_mode" { - description = "Database backend. 'rds' (default) provisions a Multi-AZ RDS instance. 'ec2' provisions a third EC2 with self-managed Postgres 16." + description = "Database backend. 'rds' (default) provisions a Multi-AZ RDS instance — recommended for production. 'ec2' provisions a third EC2 with self-managed Postgres 16 for customers that must keep data plane on EC2. Matches HAILBYTES_DB_MODE used by the Cloud Shell deploy scripts." type = string default = "rds" validation { @@ -152,7 +152,7 @@ variable "db_ec2_data_volume_size_gb" { } variable "rds_backup_retention_period" { - description = "Days RDS retains automated daily backups." + description = "Days RDS retains automated daily backups. 7 satisfies the procurement-grade baseline; raise for longer point-in-time-restore windows." type = number default = 7 } @@ -164,13 +164,13 @@ variable "rds_copy_tags_to_snapshot" { } variable "create_backup_bucket" { - description = "Provision an S3 bucket with versioning + object-lock + lifecycle for pre-patch /api/instance/export bundles." + description = "Provision an S3 bucket (versioning + object-lock governance + lifecycle to IA at 30d and Deep Archive at 90d) for pre-patch /api/instance/export bundles. The SAT instance profile gets least-privilege PutObject on hailbytes-*.tar.gz." type = bool default = true } variable "backup_bucket_name" { - description = "Name of an existing S3 bucket to use for pre-patch backups." + description = "Name of an existing S3 bucket to use for pre-patch backups. If null and create_backup_bucket is true, the module names one '-backups-'. If non-null and create_backup_bucket is false, the module only attaches the IAM PutObject policy." type = string default = null } @@ -188,13 +188,13 @@ variable "backup_noncurrent_version_expiration_days" { } variable "refresh_rollback_5xx_threshold_pct" { - description = "Target-group 5xx rate (percent) that trips the patching alarm." + description = "Target-group 5xx rate (percent) that trips the patching alarm. Default 1% over 2 evaluation periods of 1 minute." type = number default = 1 } variable "waf_web_acl_arn" { - description = "Optional ARN of an existing WAFv2 web ACL to associate with the ALB. Defaults to null." + description = "Optional ARN of an existing WAFv2 web ACL to associate with the ALB. Defaults to null (not attached). HailBytes does not bundle a managed ruleset." type = string default = null } @@ -206,7 +206,7 @@ variable "alert_email" { } variable "schema_version_endpoint_path" { - description = "Path on the ASM API that returns the running schema version." + description = "Path on the SAT/ASM API that returns the running schema version." type = string default = "/api/instance/schema-version" } @@ -238,7 +238,7 @@ variable "redis_snapshot_retention_days" { } variable "redis_endpoint_override" { - description = "Host of a customer-managed Redis endpoint. When non-null, the module skips its own ElastiCache replication group and wires the VMs at this host instead. Pair with enable_managed_redis = false." + description = "Host of a customer-managed Redis endpoint (e.g. existing ElastiCache, MemoryDB, or self-managed Redis Sentinel). When non-null, the module skips its own ElastiCache replication group and wires the VMs at this host instead. Pair with enable_managed_redis = false." type = string default = null } @@ -257,19 +257,19 @@ variable "redis_endpoint_override_tls" { variable "enable_alb_deletion_protection" { - description = "Enable deletion protection on the ALB. Default true; dev/test override to false to let `terraform destroy` succeed." + description = "Enable deletion protection on the ALB. Default true; production deployments should keep this on. Set to false in dev/test sandboxes where you want `terraform destroy` to succeed without manual cleanup." type = bool default = true } variable "enable_alb_access_logging" { - description = "Provision an S3 bucket for ALB access logs and enable the listener access_logs block." + description = "Provision an S3 bucket for ALB access logs and enable the listener access_logs block. Adds ~$1-5/mo storage cost depending on traffic; recommended for production deployments where the access log is part of the audit trail." type = bool default = false } variable "alb_access_log_retention_days" { - description = "Days to retain ALB access log objects." + description = "Days to retain ALB access log objects before lifecycle expiration. Default 365 (one calendar year) — long enough for most compliance lookback windows." type = number default = 365 } @@ -278,31 +278,31 @@ variable "alb_access_log_retention_days" { # ----- RDS production-hardening (opt-in) ----- variable "rds_enhanced_monitoring_interval" { - description = "RDS enhanced monitoring sample interval. 0 disables. CKV_AWS_118." + description = "RDS enhanced monitoring sample interval in seconds (0, 1, 5, 10, 15, 30, 60). 0 disables enhanced monitoring. Default 0; production deployments typically set 60. CKV_AWS_118." type = number default = 0 } variable "rds_enabled_cloudwatch_log_types" { - description = "RDS log types to export to CloudWatch. CKV_AWS_129." + description = "RDS log types to export to CloudWatch. Empty list = no log exports (cost-saving default). Production should set to [\"postgresql\", \"upgrade\"]. CKV_AWS_129." type = list(string) default = [] } variable "rds_iam_authentication_enabled" { - description = "Enable IAM DB authentication. CKV_AWS_161." + description = "Enable IAM database authentication on the RDS instance. Adds app-side complexity (psql connections must mint IAM tokens) but eliminates long-lived passwords. CKV_AWS_161." type = bool default = false } variable "rds_performance_insights_enabled" { - description = "Enable RDS Performance Insights. CKV_AWS_354." + description = "Enable RDS Performance Insights. Adds ~$0/instance for 7-day retention (free tier); KMS-encrypted automatically when enable_customer_managed_key is also set. CKV_AWS_354." type = bool default = false } variable "rds_performance_insights_retention_days" { - description = "Performance Insights retention. 7 = free tier; 731 = long-term." + description = "Performance Insights data retention. 7 = free tier (default); 731 = long-term retention (paid)." type = number default = 7 } diff --git a/modules/asm-aws-single/variables.tf b/modules/asm-aws-single/variables.tf index c58410b..94018e4 100644 --- a/modules/asm-aws-single/variables.tf +++ b/modules/asm-aws-single/variables.tf @@ -92,7 +92,7 @@ variable "marketplace_product_code" { # ----- Patching and migration safety ----- variable "create_backup_bucket" { - description = "Provision an S3 bucket (versioning + object-lock governance + lifecycle to IA at 30d and Deep Archive at 90d) for pre-patch /api/instance/export bundles. The instance profile gets least-privilege PutObject on hailbytes-asm-*.tar.gz." + description = "Provision an S3 bucket (versioning + object-lock governance + lifecycle to IA at 30d and Deep Archive at 90d) for pre-patch /api/instance/export bundles. The instance profile gets least-privilege PutObject on hailbytes-*.tar.gz." type = bool default = true } diff --git a/modules/asm-azure-autoscale/variables.tf b/modules/asm-azure-autoscale/variables.tf index 1c4530f..f6588d9 100644 --- a/modules/asm-azure-autoscale/variables.tf +++ b/modules/asm-azure-autoscale/variables.tf @@ -19,46 +19,51 @@ variable "admin_username" { type = string } variable "ssh_public_key" { type = string } variable "key_vault_network_default_action" { - description = "Default action for the Key Vault network ACL. 'Allow' preserves the pre-network-ACL behavior; set 'Deny' once you've added the operator IP to key_vault_ip_rules and the Microsoft.KeyVault service endpoint on vm_subnet_id." + description = "Default action for the Key Vault network ACL. 'Allow' preserves the pre-network-ACL behavior (public endpoint open, RBAC-gated); set 'Deny' once you've added the operator IP to key_vault_ip_rules and the Microsoft.KeyVault service endpoint on vm_subnet_id. AzureServices bypass is always on so the VMSS managed identity can read secrets either way." type = string default = "Allow" } variable "key_vault_ip_rules" { - description = "IPv4 addresses or CIDRs allowed to reach the Key Vault data plane. Required only when key_vault_network_default_action = Deny and you don't have Private Link configured." + description = "IPv4 addresses or CIDRs allowed to reach the Key Vault data plane (typically the operator IP running terraform apply, or your bastion's egress NAT). Required only when default_action = Deny and you don't have Private Link configured." type = list(string) default = [] } variable "associate_vm_subnet_nsg" { - description = "Associate the module-managed NSG (built from allowed_cidrs) with vm_subnet_id. Set false if the subnet already has an NSG attached by your landing-zone tooling." + description = "Associate the module-managed NSG (allow-https-* rules built from allowed_cidrs) with vm_subnet_id. Set false if the subnet already has an NSG attached and your landing-zone tooling manages ingress; the NSG ID is still exported as vmss_nsg_id for you to reference." type = bool default = true } variable "vmss_min_count" { - type = number - default = 3 + description = "Minimum number of VMSS instances." + type = number + default = 3 } variable "vmss_max_count" { - type = number - default = 20 + description = "Maximum number of VMSS instances the autoscaler can scale out to." + type = number + default = 20 } variable "vmss_default_count" { - type = number - default = 3 + description = "Starting instance count when the VMSS is created. Must be between vmss_min_count and vmss_max_count." + type = number + default = 3 } variable "vm_size" { - type = string - default = "Standard_D2s_v5" + description = "Azure VM size for VMSS instances. Standard_D2s_v5 is a balanced starting point; scale to Standard_D4s_v5 for larger tenants." + type = string + default = "Standard_D2s_v5" } variable "target_cpu_percent" { - type = number - default = 60 + description = "Target average CPU utilization (percent, 1-100) for the VMSS autoscale policy." + type = number + default = 60 } variable "db_sku_name" { @@ -67,8 +72,9 @@ variable "db_sku_name" { } variable "db_storage_mb" { - type = number - default = 262144 + description = "Storage size in MiB for the PostgreSQL Flexible Server. Minimum 32768 MiB (32 GiB); autoscaling grows in 32 GiB increments." + type = number + default = 262144 } variable "db_version" { @@ -77,13 +83,15 @@ variable "db_version" { } variable "db_backup_retention_days" { - type = number - default = 30 + description = "Days Azure Database for PostgreSQL retains automated backups. Azure Flexible Server enforces a minimum of 7 and a maximum of 35." + type = number + default = 30 } variable "db_replica_count" { - type = number - default = 2 + description = "Number of Azure Database for PostgreSQL read replicas." + type = number + default = 2 } variable "environment" { @@ -127,19 +135,19 @@ variable "create_backup_storage_account" { } variable "backup_storage_account_name" { - description = "Name of an existing Storage Account to use." + description = "Name of an existing Storage Account to use. If null and create_backup_storage_account is true, the module names one." type = string default = null } variable "backup_storage_replication" { - description = "Replication type for the backup storage account." + description = "Replication type for the backup storage account. ZRS is the procurement-grade default; GRS adds cross-region replica." type = string default = "ZRS" } variable "backup_immutability_days" { - description = "Days the immutable blob policy keeps backup objects pinned." + description = "Days the immutable blob policy keeps backup objects pinned (unlocked mode)." type = number default = 30 } @@ -157,25 +165,25 @@ variable "backup_blob_noncurrent_expiration_days" { } variable "enable_pre_patch_run_command" { - description = "Install a VMSS extension that bakes the pre-patch backup script." + description = "Install a VMSS extension that bakes the pre-patch backup script. Customers fire it via `az vmss run-command invoke` or the Portal." type = bool default = true } variable "rolling_upgrade_max_batch_percent" { - description = "VMSS rolling-upgrade batch size as a percentage of total instances." + description = "VMSS rolling-upgrade batch size as a percentage of total instances. Lower = slower, safer; 20 keeps 80% of capacity online during an upgrade batch." type = number default = 20 } variable "rolling_upgrade_max_unhealthy_percent" { - description = "Maximum percentage of unhealthy VMSS instances permitted before the upgrade pauses." + description = "Maximum percentage of unhealthy VMSS instances permitted before the upgrade pauses. Lower = stricter; this is the Azure analogue of AWS instance-refresh auto-rollback." type = number default = 20 } variable "enable_application_gateway" { - description = "Front the VMSS topology with Azure Application Gateway (required for WAF parity with AWS ALB+WAF)." + description = "Front the LB topology with an Azure Application Gateway. Required for WAF parity with the AWS ALB+WAF story." type = bool default = false } @@ -187,7 +195,7 @@ variable "appgw_subnet_id" { } variable "appgw_tls_pfx_base64" { - description = "Base64-encoded PFX for the App Gateway HTTPS listener." + description = "Base64-encoded PFX for the App Gateway HTTPS listener. Required when enable_application_gateway = true." type = string default = null sensitive = true @@ -213,19 +221,19 @@ variable "waf_policy_id" { } variable "refresh_rollback_5xx_count_threshold" { - description = "Backend 5xx response count threshold for the App Gateway rolling-upgrade tripwire." + description = "Backend 5xx response count over the alert window that trips the rolling-upgrade tripwire." type = number default = 50 } variable "schema_version_endpoint_path" { - description = "Path on the ASM API that returns the running schema version." + description = "Path on the SAT/ASM API that returns the running schema version." type = string default = "/api/instance/schema-version" } variable "enable_post_patch_run_command" { - description = "Install a VMSS extension named RunPostPatchVerify mirroring the AWS asm-aws-autoscale aws_ssm_document.post_patch_verify." + description = "Install a VMSS extension named RunPostPatchVerify that runs the on-VM five-probe verifier, mirroring the AWS aws_ssm_document.post_patch_verify." type = bool default = true } @@ -233,30 +241,31 @@ variable "enable_post_patch_run_command" { # ----- Shared session store (Azure Cache for Redis) ----- variable "enable_managed_redis" { - description = "Provision an Azure Cache for Redis. Required for horizontal scaling; set to false only when supplying redis_endpoint_override." + description = "Provision an Azure Cache for Redis (Standard or Premium SKU). Required for horizontal scaling; set to false only when supplying redis_endpoint_override." type = bool default = true } variable "redis_sku_name" { - description = "Redis SKU. Standard or Premium only (Basic is single-node)." + description = "Redis SKU. Standard delivers a primary/replica pair; Premium adds zone selection. Basic is single-node and breaks horizontal scaling (validated)." type = string default = "Standard" } variable "redis_family" { - type = string - default = "C" + description = "Redis SKU family. 'C' = Standard/Basic, 'P' = Premium." + type = string + default = "C" } variable "redis_capacity" { - description = "Redis capacity (size index). 0-6 for Standard. Scale alongside VMSS instance count." + description = "Redis capacity (size index). For SKU=Standard / family=C, valid values are 0-6. Scale alongside VMSS instance count: 1 (1GB) handles 3-5 instances; 3 (6GB) handles 10-20+." type = number default = 1 } variable "redis_endpoint_override" { - description = "Host of an existing customer-managed Redis endpoint." + description = "Host of an existing customer-managed Redis endpoint. Pair with enable_managed_redis = false." type = string default = null } @@ -273,14 +282,14 @@ variable "redis_endpoint_override_tls" { variable "db_secret_expiration_hours" { - description = "Hours until the Key Vault DB-password secret expires. Default 8760 = one calendar year." + description = "Hours until the Key Vault DB-password secret expires. Default 8760 = one calendar year. Set on every apply via timeadd(timestamp(), ...) and then ignored on subsequent applies so a stale value doesn't show drift." type = number default = 8760 } variable "postgres_geo_redundant_backup_enabled" { - description = "Enable geo-redundant backup on Postgres Flexible Server. CKV_AZURE_136." + description = "Enable geo-redundant backup on the Postgres Flexible Server. Defaults to false; adds cross-region replication of backups for DR scenarios. CKV_AZURE_136." type = bool default = false } diff --git a/modules/asm-azure-ha/variables.tf b/modules/asm-azure-ha/variables.tf index 0bbd76b..6c7d582 100644 --- a/modules/asm-azure-ha/variables.tf +++ b/modules/asm-azure-ha/variables.tf @@ -43,13 +43,13 @@ variable "ssh_public_key" { } variable "key_vault_network_default_action" { - description = "Default action for the Key Vault network ACL. 'Allow' preserves the pre-network-ACL behavior; set 'Deny' once you've added the operator IP to key_vault_ip_rules and the Microsoft.KeyVault service endpoint on vm_subnet_id." + description = "Default action for the Key Vault network ACL. 'Allow' preserves the pre-network-ACL behavior (public endpoint open, RBAC-gated); set 'Deny' once you've added the operator IP to key_vault_ip_rules and the Microsoft.KeyVault service endpoint on vm_subnet_id. AzureServices bypass is always on so the VMs' managed identities can read secrets either way." type = string default = "Allow" } variable "key_vault_ip_rules" { - description = "IPv4 addresses or CIDRs allowed to reach the Key Vault data plane. Required only when key_vault_network_default_action = Deny and you don't have Private Link configured." + description = "IPv4 addresses or CIDRs allowed to reach the Key Vault data plane (typically the operator IP running terraform apply, or your bastion's egress NAT). Required only when default_action = Deny and you don't have Private Link configured." type = list(string) default = [] } @@ -75,7 +75,7 @@ variable "data_disk_size_gb" { } variable "enable_customer_managed_key" { - description = "Encrypt VM OS and data disks with a customer-managed key (RSA-4096 in the module's Key Vault, via a disk encryption set) instead of platform-managed keys." + description = "Encrypt VM OS and data disks with a customer-managed key (RSA-4096 in this module's Key Vault, via a disk encryption set) instead of platform-managed keys. Matches the single-vm tier's CMK option." type = bool default = false } @@ -127,7 +127,7 @@ variable "marketplace_image_version" { # ----- Patching and migration safety ----- variable "db_mode" { - description = "Database backend. 'flexible_server' (default) or 'vm' for self-managed Postgres on a Linux VM." + description = "Database backend. 'flexible_server' (default) provisions Azure Database for PostgreSQL Flexible Server — recommended for production. 'vm' provisions a third Linux VM with self-managed Postgres 16 for customers that must keep data plane on a VM." type = string default = "flexible_server" validation { @@ -155,19 +155,19 @@ variable "create_backup_storage_account" { } variable "backup_storage_account_name" { - description = "Name of an existing Storage Account to use." + description = "Name of an existing Storage Account to use. If null and create_backup_storage_account is true, the module names one." type = string default = null } variable "backup_storage_replication" { - description = "Replication type for the backup storage account." + description = "Replication type for the backup storage account. ZRS is the procurement-grade default." type = string default = "ZRS" } variable "backup_immutability_days" { - description = "Days the immutable blob policy keeps backup objects pinned." + description = "Days the immutable blob policy keeps backup objects pinned (unlocked mode so customers can extend later)." type = number default = 30 } @@ -185,45 +185,45 @@ variable "backup_blob_noncurrent_expiration_days" { } variable "enable_pre_patch_run_command" { - description = "Install an Azure Run Command document named RunPrePatchBackup." + description = "Install an Azure Run Command document named RunPrePatchBackup on the first SAT VM. Customers fire it from the Portal." type = bool default = true } variable "enable_application_gateway" { - description = "Front the LB topology with Azure Application Gateway (required for WAF parity with AWS ALB+WAF)." + description = "Front the LB topology with an Azure Application Gateway. Required if you want WAF parity with the AWS ALB+WAF story; the existing Standard LB is L4-only and cannot host WAF rules." type = bool default = false } variable "appgw_subnet_id" { - description = "Subnet for the Application Gateway. Required when enable_application_gateway = true." + description = "Subnet for the Application Gateway. Required when enable_application_gateway = true. Must be /24 or larger, in the same vnet as the VMs." type = string default = null } variable "appgw_tls_pfx_base64" { - description = "Base64-encoded PFX for the App Gateway HTTPS listener." + description = "Base64-encoded PFX bundle for the App Gateway HTTPS listener. Required when enable_application_gateway = true." type = string default = null sensitive = true } variable "appgw_tls_pfx_password" { - description = "Password for the PFX bundle." + description = "Password for the PFX bundle. Required when enable_application_gateway = true if the PFX is encrypted." type = string default = null sensitive = true } variable "appgw_backend_host_header" { - description = "Optional Host header App Gateway sends to the ASM backend." + description = "Optional Host header App Gateway sends to the SAT backend pool. Leave null to use the backend's IP." type = string default = null } variable "waf_policy_id" { - description = "Optional ID of an azurerm_web_application_firewall_policy to attach to the App Gateway." + description = "Optional ID of an azurerm_web_application_firewall_policy to attach to the App Gateway. HailBytes does not bundle a managed ruleset; most enterprises bring their own." type = string default = null } @@ -235,19 +235,19 @@ variable "alert_email" { } variable "refresh_rollback_5xx_count_threshold" { - description = "Backend 5xx response count threshold for the App Gateway alarm." + description = "Backend 5xx response count over 5 minutes that trips the patching alarm on the Application Gateway. Tune for site traffic; the Azure metric is a count, not a rate." type = number default = 10 } variable "schema_version_endpoint_path" { - description = "Path on the ASM API that returns the running schema version." + description = "Path on the SAT/ASM API that returns the running schema version." type = string default = "/api/instance/schema-version" } variable "enable_post_patch_run_command" { - description = "Install an Azure Run Command document named RunPostPatchVerify on each VM, mirroring the AWS asm-aws-ha aws_ssm_document.post_patch_verify." + description = "Install an Azure Run Command document named RunPostPatchVerify on each VM, mirroring the AWS aws_ssm_document.post_patch_verify in the SAT/ASM aws-ha modules. Customers fire it from the Portal after a Run Command-driven image swap." type = bool default = true } @@ -261,7 +261,7 @@ variable "enable_managed_redis" { } variable "redis_sku_name" { - description = "Redis SKU. Standard delivers a primary/replica pair across two zones; Premium adds persistence and explicit zone selection. Basic is single-node and NOT a valid HA option." + description = "Redis SKU. Standard delivers a primary/replica pair across two zones; Premium adds persistence and explicit zone selection. Basic is single-node and NOT a valid HA option (validated)." type = string default = "Standard" } @@ -273,7 +273,7 @@ variable "redis_family" { } variable "redis_capacity" { - description = "Redis capacity (size index). For SKU=Standard / family=C, valid values are 0 (250MB) through 6 (53GB). The procurement-friendly default is 1 (1GB)." + description = "Redis capacity (size index). For SKU=Standard / family=C, valid values are 0 (250MB) through 6 (53GB). cache.t4g.small-equivalent is 1 (1GB)." type = number default = 1 } @@ -285,25 +285,27 @@ variable "redis_endpoint_override" { } variable "redis_endpoint_override_port" { - type = number - default = 6380 + description = "Port on the customer-managed Redis endpoint. 6380 (TLS) is the Azure default. Ignored unless redis_endpoint_override is set." + type = number + default = 6380 } variable "redis_endpoint_override_tls" { - type = bool - default = true + description = "Whether the customer-managed Redis endpoint requires in-transit TLS. Ignored unless redis_endpoint_override is set." + type = bool + default = true } variable "db_secret_expiration_hours" { - description = "Hours until the Key Vault DB-password secret expires. Default 8760 = one calendar year." + description = "Hours until the Key Vault DB-password secret expires. Set on every apply via `timeadd(timestamp(), ...)` and then ignored on subsequent applies so a stale value doesn't show drift. Default 8760 = one calendar year — long enough that ops don't need to rotate weekly, short enough that the secret never lives unrotated past a year without operator attention." type = number default = 8760 } variable "postgres_geo_redundant_backup_enabled" { - description = "Enable geo-redundant backup on Postgres Flexible Server. CKV_AZURE_136." + description = "Enable geo-redundant backup on the Postgres Flexible Server. Defaults to false; adds cross-region replication of backups for DR scenarios. CKV_AZURE_136." type = bool default = false } diff --git a/modules/asm-azure-single/variables.tf b/modules/asm-azure-single/variables.tf index 7aebf7a..591d17f 100644 --- a/modules/asm-azure-single/variables.tf +++ b/modules/asm-azure-single/variables.tf @@ -107,25 +107,25 @@ variable "marketplace_image_version" { # ----- Patching and migration safety ----- variable "create_backup_storage_account" { - description = "Provision a Storage Account + immutable container for pre-patch /api/instance/export bundles." + description = "Provision an Azure Storage Account + container (blob versioning + immutable WORM policy in unlocked mode + lifecycle to Cool at 30d and Archive at 90d) for pre-patch /api/instance/export bundles. The VM's system-assigned managed identity gets Storage Blob Data Contributor on the container only." type = bool default = true } variable "backup_storage_account_name" { - description = "Name of an existing Storage Account to use. If null and create_backup_storage_account is true, the module names one." + description = "Name of an existing Storage Account to use. If null and create_backup_storage_account is true, the module names one (lowercase, alphanumeric, max 24 chars). If non-null and create_backup_storage_account is false, the module only grants the managed identity blob writer perms on it." type = string default = null } variable "backup_storage_replication" { - description = "Replication type for the backup storage account. ZRS is the procurement-grade default." + description = "Replication type for the backup storage account. ZRS (zone-redundant) is the recommended default for procurement-grade durability. GRS adds cross-region replica." type = string default = "ZRS" } variable "backup_immutability_days" { - description = "Days the immutable blob policy keeps backup objects pinned (unlocked mode)." + description = "Days the immutable blob policy keeps backup objects pinned. Set in unlocked mode so customers can extend later through portal/CLI." type = number default = 30 } @@ -143,7 +143,7 @@ variable "backup_blob_noncurrent_expiration_days" { } variable "enable_pre_patch_run_command" { - description = "Install an Azure Run Command document named RunPrePatchBackup." + description = "Install an Azure Run Command document named RunPrePatchBackup. Customers can fire it from the Portal (VM -> Operations -> Run command) to take a pre-patch backup + managed-disk snapshot in one click. Disable if your AMI does not yet bundle ha-pre-patch-backup.sh." type = bool default = true } diff --git a/modules/sat-aws-autoscale/variables.tf b/modules/sat-aws-autoscale/variables.tf index 83698be..ba7e580 100644 --- a/modules/sat-aws-autoscale/variables.tf +++ b/modules/sat-aws-autoscale/variables.tf @@ -3,11 +3,13 @@ # which is hardcoded by this wrapper). variable "vpc_id" { - type = string + description = "VPC to deploy into." + type = string } variable "public_subnet_ids" { - type = list(string) + description = "At least two public subnet IDs in different AZs for the ALB." + type = list(string) validation { condition = length(var.public_subnet_ids) >= 2 error_message = "At least two public subnets are required." @@ -15,7 +17,8 @@ variable "public_subnet_ids" { } variable "private_subnet_ids" { - type = list(string) + description = "At least three private subnet IDs, one per AZ, for ASG instances, RDS, and ElastiCache." + type = list(string) validation { condition = length(var.private_subnet_ids) >= 3 error_message = "At least three private subnets (one per AZ) are required for unlimited-scale." @@ -23,11 +26,13 @@ variable "private_subnet_ids" { } variable "allowed_cidrs" { - type = list(string) + description = "CIDR blocks permitted to reach the ALB on port 443 (and port 80 when enable_http_redirect is true)." + type = list(string) } variable "acm_certificate_arn" { - type = string + description = "ARN of an ACM certificate in the same region for the ALB HTTPS listener." + type = string } variable "alert_email" { @@ -37,98 +42,117 @@ variable "alert_email" { } variable "asg_min_size" { - type = number - default = 3 + description = "Minimum number of instances the ASG maintains." + type = number + default = 3 } variable "asg_max_size" { - type = number - default = 20 + description = "Maximum number of instances the ASG can scale out to." + type = number + default = 20 } variable "asg_desired_capacity" { - type = number - default = 3 + description = "Starting instance count when the ASG is created. Must be between asg_min_size and asg_max_size." + type = number + default = 3 } variable "instance_type" { - type = string - default = "m6i.large" + description = "EC2 instance type for the ASG launch template. m6i.large is a balanced starting point; scale to m6i.xlarge for larger tenants." + type = string + default = "m6i.large" } variable "target_cpu_utilization" { - type = number - default = 60 + description = "Target average CPU utilization (percent, 1-100) for the ASG target-tracking scale-out policy." + type = number + default = 60 } variable "target_request_count_per_target" { - type = number - default = 500 + description = "Target ALB request count per instance for the request-count scale-out policy." + type = number + default = 500 } variable "db_instance_class" { - type = string - default = "db.r6g.large" + description = "RDS instance class. db.r6g.large handles up to ~200 concurrent connections; scale to r6g.xlarge for larger deployments." + type = string + default = "db.r6g.large" } variable "db_allocated_storage_gb" { - type = number - default = 200 + description = "Initial allocated storage for the RDS instance in GiB." + type = number + default = 200 } variable "db_max_allocated_storage_gb" { - type = number - default = 2000 + description = "Upper limit for RDS storage autoscaling in GiB. Must be >= db_allocated_storage_gb." + type = number + default = 2000 } variable "db_engine_version" { - type = string - default = "16.4" + description = "PostgreSQL engine version. Pin to a specific minor version (e.g. 16.4) to prevent unexpected minor upgrades during terraform apply." + type = string + default = "16.4" } variable "db_backup_retention_days" { - type = number - default = 30 + description = "Days RDS retains automated daily backups. 30 covers a typical monthly review cycle and aligns with the pre-patch on-demand snapshot lifecycle." + type = number + default = 30 } variable "db_read_replica_count" { - type = number - default = 2 + description = "Number of RDS read replicas to create. Replicas reduce read load and provide manual failover targets." + type = number + default = 2 } variable "db_deletion_protection" { - type = bool - default = true + description = "Enable RDS deletion protection. Default true; set false only in dev/test sandboxes where terraform destroy should succeed." + type = bool + default = true } variable "environment" { - type = string - default = "prod" + description = "Short environment label (e.g. prod, staging) appended to resource names and tags." + type = string + default = "prod" } variable "name_prefix" { - type = string - default = null + description = "Optional prefix prepended to all resource names. Defaults to '-unlimited-scale' when null." + type = string + default = null } variable "enable_customer_managed_key" { - type = bool - default = true + description = "Create a KMS customer-managed key for encrypting RDS, ElastiCache, and S3 backup objects. Disable only if your organization manages keys externally." + type = bool + default = true } variable "alb_min_tls_version" { - type = string - default = "ELBSecurityPolicy-TLS13-1-2-2021-06" + description = "ELBSecurityPolicy name defining the minimum TLS version and cipher suite for the ALB HTTPS listener." + type = string + default = "ELBSecurityPolicy-TLS13-1-2-2021-06" } variable "enable_flow_logs" { - type = bool - default = true + description = "Publish VPC Flow Logs to CloudWatch. Recommended for production compliance." + type = bool + default = true } variable "access_log_retention_days" { - type = number - default = 90 + description = "CloudWatch log retention in days for ALB access logs and VPC Flow Logs." + type = number + default = 90 } variable "marketplace_product_code" { @@ -146,49 +170,49 @@ variable "enable_http_redirect" { # ----- Patching and migration safety ----- variable "create_backup_bucket" { - description = "Provision an S3 bucket with versioning + object-lock + lifecycle for pre-patch /api/instance/export bundles." + description = "Provision an S3 bucket (versioning + object-lock governance + lifecycle to IA at 30d and Deep Archive at 90d) for pre-patch /api/instance/export bundles. The SAT instance profile gets least-privilege PutObject on hailbytes-*.tar.gz." type = bool default = true } variable "backup_bucket_name" { - description = "Name of an existing S3 bucket to use for pre-patch backups." + description = "Name of an existing S3 bucket to use for pre-patch backups. If null and create_backup_bucket is true, the module names one '-backups-'. If non-null and create_backup_bucket is false, the module only attaches the IAM PutObject policy to the existing bucket." type = string default = null } variable "backup_object_lock_retention_days" { - description = "Object Lock (governance mode) retention period for backup objects." + description = "Object Lock (governance mode) retention period for backup objects. 30 days satisfies the procurement-grade safety net while still allowing privileged operator override for compaction." type = number default = 30 } variable "backup_noncurrent_version_expiration_days" { - description = "Expire noncurrent versions of backup objects after this many days." + description = "Expire noncurrent versions of backup objects after this many days. 365 retains a year of pre-patch bundles for rollback." type = number default = 365 } variable "instance_refresh_min_healthy_percentage" { - description = "Minimum percentage of the ASG that must remain healthy during an instance refresh." + description = "Minimum percentage of the ASG that must remain healthy during an instance refresh. 50 drains one instance at a time on a 2-instance ASG; tune higher for larger fleets that can tolerate parallel replacement." type = number default = 50 } variable "instance_refresh_instance_warmup_seconds" { - description = "Seconds the ASG considers a new instance 'warming up' before counting toward healthy_percentage." + description = "Seconds the ASG considers a new instance 'warming up' before counting toward healthy_percentage. 120 is enough for the SAT marketplace AMI to pass the ALB /health probe; raise for slower boot images." type = number default = 120 } variable "refresh_rollback_5xx_threshold_pct" { - description = "Target-group 5xx rate (percent) that triggers instance-refresh auto-rollback." + description = "Target-group 5xx rate (percent) above which the instance refresh auto-rollback alarm fires. Default 1% over 2 evaluation periods of 1 minute." type = number default = 1 } variable "waf_web_acl_arn" { - description = "Optional ARN of an existing WAFv2 web ACL to associate with the ALB." + description = "Optional ARN of an existing WAFv2 web ACL to associate with the ALB. Defaults to null (not attached). HailBytes does not bundle a managed ruleset; most enterprises bring their own." type = string default = null } @@ -200,7 +224,7 @@ variable "rds_copy_tags_to_snapshot" { } variable "schema_version_endpoint_path" { - description = "Path on the SAT API that returns the running schema version." + description = "Path on the SAT/ASM API that returns the running schema version. Used by the schema_version_endpoint output that customer CI/CD curls in post-patch verification." type = string default = "/api/instance/schema-version" } @@ -220,13 +244,15 @@ variable "redis_node_type" { } variable "redis_engine_version" { - type = string - default = "7.1" + description = "Redis engine version for the ElastiCache replication group." + type = string + default = "7.1" } variable "redis_snapshot_retention_days" { - type = number - default = 0 + description = "Days ElastiCache retains automatic snapshots. 0 disables snapshots." + type = number + default = 0 } variable "redis_endpoint_override" { @@ -236,18 +262,20 @@ variable "redis_endpoint_override" { } variable "redis_endpoint_override_port" { - type = number - default = 6379 + description = "TCP port for the customer-managed Redis endpoint supplied via redis_endpoint_override." + type = number + default = 6379 } variable "redis_endpoint_override_tls" { - type = bool - default = true + description = "Whether to connect to the customer-managed Redis endpoint over TLS." + type = bool + default = true } variable "enable_alb_deletion_protection" { - description = "Enable deletion protection on the ALB. Default true." + description = "Enable deletion protection on the ALB. Default true; production deployments should keep this on. Set to false in dev/test sandboxes where `terraform destroy` should succeed without manual cleanup." type = bool default = true } @@ -256,19 +284,19 @@ variable "enable_alb_deletion_protection" { # ----- RDS production-hardening (opt-in) ----- variable "rds_enhanced_monitoring_interval" { - description = "RDS enhanced monitoring sample interval. 0 disables. CKV_AWS_118." + description = "RDS enhanced monitoring sample interval in seconds. 0 disables. Default 0; production typically 60. CKV_AWS_118." type = number default = 0 } variable "rds_enabled_cloudwatch_log_types" { - description = "RDS log types to export to CloudWatch. CKV_AWS_129." + description = "RDS log types to export to CloudWatch. Empty list = none (cost-saving default). Production should set [\"postgresql\", \"upgrade\"]. CKV_AWS_129." type = list(string) default = [] } variable "rds_iam_authentication_enabled" { - description = "Enable IAM DB authentication. CKV_AWS_161." + description = "Enable IAM database authentication on RDS. CKV_AWS_161." type = bool default = false } @@ -280,12 +308,13 @@ variable "rds_performance_insights_enabled" { } variable "rds_performance_insights_retention_days" { - description = "Performance Insights retention. 7 = free tier; 731 = long-term." + description = "Performance Insights data retention. 7 = free tier (default); 731 = long-term." type = number default = 7 } variable "tags" { - type = map(string) - default = {} + description = "Additional tags merged on to every taggable resource." + type = map(string) + default = {} } diff --git a/modules/sat-aws-ha/variables.tf b/modules/sat-aws-ha/variables.tf index 04cc02b..34fec44 100644 --- a/modules/sat-aws-ha/variables.tf +++ b/modules/sat-aws-ha/variables.tf @@ -83,7 +83,7 @@ variable "db_engine_version" { } variable "db_backup_retention_days" { - description = "Deprecated alias for rds_backup_retention_period. Kept for backward compatibility." + description = "Deprecated alias for rds_backup_retention_period. When set (non-null), this value wins over rds_backup_retention_period. Leave null and use rds_backup_retention_period instead. Kept for backward compatibility with pre-patching-safety configs." type = number default = null } @@ -130,7 +130,7 @@ variable "enable_http_redirect" { # ----- Patching and migration safety ----- variable "db_mode" { - description = "Database backend. 'rds' (default) provisions a Multi-AZ RDS instance. 'ec2' provisions a third EC2 with self-managed Postgres 16." + description = "Database backend. 'rds' (default) provisions a Multi-AZ RDS instance — recommended for production. 'ec2' provisions a third EC2 with self-managed Postgres 16 for customers that must keep data plane on EC2. Matches HAILBYTES_DB_MODE used by the Cloud Shell deploy scripts." type = string default = "rds" validation { @@ -152,7 +152,7 @@ variable "db_ec2_data_volume_size_gb" { } variable "rds_backup_retention_period" { - description = "Days RDS retains automated daily backups." + description = "Days RDS retains automated daily backups. 7 satisfies the procurement-grade baseline; raise for longer point-in-time-restore windows." type = number default = 7 } @@ -164,13 +164,13 @@ variable "rds_copy_tags_to_snapshot" { } variable "create_backup_bucket" { - description = "Provision an S3 bucket with versioning + object-lock + lifecycle for pre-patch /api/instance/export bundles." + description = "Provision an S3 bucket (versioning + object-lock governance + lifecycle to IA at 30d and Deep Archive at 90d) for pre-patch /api/instance/export bundles. The SAT instance profile gets least-privilege PutObject on hailbytes-*.tar.gz." type = bool default = true } variable "backup_bucket_name" { - description = "Name of an existing S3 bucket to use for pre-patch backups." + description = "Name of an existing S3 bucket to use for pre-patch backups. If null and create_backup_bucket is true, the module names one '-backups-'. If non-null and create_backup_bucket is false, the module only attaches the IAM PutObject policy." type = string default = null } @@ -188,13 +188,13 @@ variable "backup_noncurrent_version_expiration_days" { } variable "refresh_rollback_5xx_threshold_pct" { - description = "Target-group 5xx rate (percent) that trips the patching alarm." + description = "Target-group 5xx rate (percent) that trips the patching alarm. Default 1% over 2 evaluation periods of 1 minute." type = number default = 1 } variable "waf_web_acl_arn" { - description = "Optional ARN of an existing WAFv2 web ACL to associate with the ALB. Defaults to null." + description = "Optional ARN of an existing WAFv2 web ACL to associate with the ALB. Defaults to null (not attached). HailBytes does not bundle a managed ruleset." type = string default = null } @@ -206,7 +206,7 @@ variable "alert_email" { } variable "schema_version_endpoint_path" { - description = "Path on the SAT API that returns the running schema version." + description = "Path on the SAT/ASM API that returns the running schema version." type = string default = "/api/instance/schema-version" } @@ -238,7 +238,7 @@ variable "redis_snapshot_retention_days" { } variable "redis_endpoint_override" { - description = "Host of a customer-managed Redis endpoint. When non-null, the module skips its own ElastiCache replication group and wires the VMs at this host instead. Pair with enable_managed_redis = false." + description = "Host of a customer-managed Redis endpoint (e.g. existing ElastiCache, MemoryDB, or self-managed Redis Sentinel). When non-null, the module skips its own ElastiCache replication group and wires the VMs at this host instead. Pair with enable_managed_redis = false." type = string default = null } @@ -257,19 +257,19 @@ variable "redis_endpoint_override_tls" { variable "enable_alb_deletion_protection" { - description = "Enable deletion protection on the ALB. Default true; dev/test override to false to let `terraform destroy` succeed." + description = "Enable deletion protection on the ALB. Default true; production deployments should keep this on. Set to false in dev/test sandboxes where you want `terraform destroy` to succeed without manual cleanup." type = bool default = true } variable "enable_alb_access_logging" { - description = "Provision an S3 bucket for ALB access logs and enable the listener access_logs block." + description = "Provision an S3 bucket for ALB access logs and enable the listener access_logs block. Adds ~$1-5/mo storage cost depending on traffic; recommended for production deployments where the access log is part of the audit trail." type = bool default = false } variable "alb_access_log_retention_days" { - description = "Days to retain ALB access log objects." + description = "Days to retain ALB access log objects before lifecycle expiration. Default 365 (one calendar year) — long enough for most compliance lookback windows." type = number default = 365 } @@ -278,31 +278,31 @@ variable "alb_access_log_retention_days" { # ----- RDS production-hardening (opt-in) ----- variable "rds_enhanced_monitoring_interval" { - description = "RDS enhanced monitoring sample interval. 0 disables. CKV_AWS_118." + description = "RDS enhanced monitoring sample interval in seconds (0, 1, 5, 10, 15, 30, 60). 0 disables enhanced monitoring. Default 0; production deployments typically set 60. CKV_AWS_118." type = number default = 0 } variable "rds_enabled_cloudwatch_log_types" { - description = "RDS log types to export to CloudWatch. CKV_AWS_129." + description = "RDS log types to export to CloudWatch. Empty list = no log exports (cost-saving default). Production should set to [\"postgresql\", \"upgrade\"]. CKV_AWS_129." type = list(string) default = [] } variable "rds_iam_authentication_enabled" { - description = "Enable IAM DB authentication. CKV_AWS_161." + description = "Enable IAM database authentication on the RDS instance. Adds app-side complexity (psql connections must mint IAM tokens) but eliminates long-lived passwords. CKV_AWS_161." type = bool default = false } variable "rds_performance_insights_enabled" { - description = "Enable RDS Performance Insights. CKV_AWS_354." + description = "Enable RDS Performance Insights. Adds ~$0/instance for 7-day retention (free tier); KMS-encrypted automatically when enable_customer_managed_key is also set. CKV_AWS_354." type = bool default = false } variable "rds_performance_insights_retention_days" { - description = "Performance Insights retention. 7 = free tier; 731 = long-term." + description = "Performance Insights data retention. 7 = free tier (default); 731 = long-term retention (paid)." type = number default = 7 } diff --git a/modules/sat-aws-single/variables.tf b/modules/sat-aws-single/variables.tf index 38db5b9..04b6bc3 100644 --- a/modules/sat-aws-single/variables.tf +++ b/modules/sat-aws-single/variables.tf @@ -92,7 +92,7 @@ variable "marketplace_product_code" { # ----- Patching and migration safety ----- variable "create_backup_bucket" { - description = "Provision an S3 bucket (versioning + object-lock governance + lifecycle to IA at 30d and Deep Archive at 90d) for pre-patch /api/instance/export bundles. The instance profile gets least-privilege PutObject on hailbytes-sat-*.tar.gz." + description = "Provision an S3 bucket (versioning + object-lock governance + lifecycle to IA at 30d and Deep Archive at 90d) for pre-patch /api/instance/export bundles. The instance profile gets least-privilege PutObject on hailbytes-*.tar.gz." type = bool default = true } diff --git a/modules/sat-azure-autoscale/variables.tf b/modules/sat-azure-autoscale/variables.tf index aec9ec3..7cc2491 100644 --- a/modules/sat-azure-autoscale/variables.tf +++ b/modules/sat-azure-autoscale/variables.tf @@ -19,46 +19,51 @@ variable "admin_username" { type = string } variable "ssh_public_key" { type = string } variable "key_vault_network_default_action" { - description = "Default action for the Key Vault network ACL. 'Allow' preserves the pre-network-ACL behavior; set 'Deny' once you've added the operator IP to key_vault_ip_rules and the Microsoft.KeyVault service endpoint on vm_subnet_id." + description = "Default action for the Key Vault network ACL. 'Allow' preserves the pre-network-ACL behavior (public endpoint open, RBAC-gated); set 'Deny' once you've added the operator IP to key_vault_ip_rules and the Microsoft.KeyVault service endpoint on vm_subnet_id. AzureServices bypass is always on so the VMSS managed identity can read secrets either way." type = string default = "Allow" } variable "key_vault_ip_rules" { - description = "IPv4 addresses or CIDRs allowed to reach the Key Vault data plane. Required only when key_vault_network_default_action = Deny and you don't have Private Link configured." + description = "IPv4 addresses or CIDRs allowed to reach the Key Vault data plane (typically the operator IP running terraform apply, or your bastion's egress NAT). Required only when default_action = Deny and you don't have Private Link configured." type = list(string) default = [] } variable "associate_vm_subnet_nsg" { - description = "Associate the module-managed NSG (built from allowed_cidrs) with vm_subnet_id. Set false if the subnet already has an NSG attached by your landing-zone tooling." + description = "Associate the module-managed NSG (allow-https-* rules built from allowed_cidrs) with vm_subnet_id. Set false if the subnet already has an NSG attached and your landing-zone tooling manages ingress; the NSG ID is still exported as vmss_nsg_id for you to reference." type = bool default = true } variable "vmss_min_count" { - type = number - default = 3 + description = "Minimum number of VMSS instances." + type = number + default = 3 } variable "vmss_max_count" { - type = number - default = 20 + description = "Maximum number of VMSS instances the autoscaler can scale out to." + type = number + default = 20 } variable "vmss_default_count" { - type = number - default = 3 + description = "Starting instance count when the VMSS is created. Must be between vmss_min_count and vmss_max_count." + type = number + default = 3 } variable "vm_size" { - type = string - default = "Standard_D2s_v5" + description = "Azure VM size for VMSS instances. Standard_D2s_v5 is a balanced starting point; scale to Standard_D4s_v5 for larger tenants." + type = string + default = "Standard_D2s_v5" } variable "target_cpu_percent" { - type = number - default = 60 + description = "Target average CPU utilization (percent, 1-100) for the VMSS autoscale policy." + type = number + default = 60 } variable "db_sku_name" { @@ -67,8 +72,9 @@ variable "db_sku_name" { } variable "db_storage_mb" { - type = number - default = 262144 + description = "Storage size in MiB for the PostgreSQL Flexible Server. Minimum 32768 MiB (32 GiB); autoscaling grows in 32 GiB increments." + type = number + default = 262144 } variable "db_version" { @@ -77,13 +83,15 @@ variable "db_version" { } variable "db_backup_retention_days" { - type = number - default = 30 + description = "Days Azure Database for PostgreSQL retains automated backups. Azure Flexible Server enforces a minimum of 7 and a maximum of 35." + type = number + default = 30 } variable "db_replica_count" { - type = number - default = 2 + description = "Number of Azure Database for PostgreSQL read replicas." + type = number + default = 2 } variable "environment" { @@ -127,19 +135,19 @@ variable "create_backup_storage_account" { } variable "backup_storage_account_name" { - description = "Name of an existing Storage Account to use." + description = "Name of an existing Storage Account to use. If null and create_backup_storage_account is true, the module names one." type = string default = null } variable "backup_storage_replication" { - description = "Replication type for the backup storage account." + description = "Replication type for the backup storage account. ZRS is the procurement-grade default; GRS adds cross-region replica." type = string default = "ZRS" } variable "backup_immutability_days" { - description = "Days the immutable blob policy keeps backup objects pinned." + description = "Days the immutable blob policy keeps backup objects pinned (unlocked mode)." type = number default = 30 } @@ -157,25 +165,25 @@ variable "backup_blob_noncurrent_expiration_days" { } variable "enable_pre_patch_run_command" { - description = "Install a VMSS extension that bakes the pre-patch backup script." + description = "Install a VMSS extension that bakes the pre-patch backup script. Customers fire it via `az vmss run-command invoke` or the Portal." type = bool default = true } variable "rolling_upgrade_max_batch_percent" { - description = "VMSS rolling-upgrade batch size as a percentage of total instances." + description = "VMSS rolling-upgrade batch size as a percentage of total instances. Lower = slower, safer; 20 keeps 80% of capacity online during an upgrade batch." type = number default = 20 } variable "rolling_upgrade_max_unhealthy_percent" { - description = "Maximum percentage of unhealthy VMSS instances permitted before the upgrade pauses." + description = "Maximum percentage of unhealthy VMSS instances permitted before the upgrade pauses. Lower = stricter; this is the Azure analogue of AWS instance-refresh auto-rollback." type = number default = 20 } variable "enable_application_gateway" { - description = "Front the VMSS topology with Azure Application Gateway (required for WAF parity with AWS ALB+WAF)." + description = "Front the LB topology with an Azure Application Gateway. Required for WAF parity with the AWS ALB+WAF story." type = bool default = false } @@ -187,7 +195,7 @@ variable "appgw_subnet_id" { } variable "appgw_tls_pfx_base64" { - description = "Base64-encoded PFX for the App Gateway HTTPS listener." + description = "Base64-encoded PFX for the App Gateway HTTPS listener. Required when enable_application_gateway = true." type = string default = null sensitive = true @@ -213,19 +221,19 @@ variable "waf_policy_id" { } variable "refresh_rollback_5xx_count_threshold" { - description = "Backend 5xx response count threshold for the App Gateway rolling-upgrade tripwire." + description = "Backend 5xx response count over the alert window that trips the rolling-upgrade tripwire." type = number default = 50 } variable "schema_version_endpoint_path" { - description = "Path on the SAT API that returns the running schema version." + description = "Path on the SAT/ASM API that returns the running schema version." type = string default = "/api/instance/schema-version" } variable "enable_post_patch_run_command" { - description = "Install a VMSS extension named RunPostPatchVerify mirroring the AWS sat-aws-autoscale aws_ssm_document.post_patch_verify." + description = "Install a VMSS extension named RunPostPatchVerify that runs the on-VM five-probe verifier, mirroring the AWS aws_ssm_document.post_patch_verify." type = bool default = true } @@ -233,30 +241,31 @@ variable "enable_post_patch_run_command" { # ----- Shared session store (Azure Cache for Redis) ----- variable "enable_managed_redis" { - description = "Provision an Azure Cache for Redis. Required for horizontal scaling; set to false only when supplying redis_endpoint_override." + description = "Provision an Azure Cache for Redis (Standard or Premium SKU). Required for horizontal scaling; set to false only when supplying redis_endpoint_override." type = bool default = true } variable "redis_sku_name" { - description = "Redis SKU. Standard or Premium only (Basic is single-node)." + description = "Redis SKU. Standard delivers a primary/replica pair; Premium adds zone selection. Basic is single-node and breaks horizontal scaling (validated)." type = string default = "Standard" } variable "redis_family" { - type = string - default = "C" + description = "Redis SKU family. 'C' = Standard/Basic, 'P' = Premium." + type = string + default = "C" } variable "redis_capacity" { - description = "Redis capacity (size index). 0-6 for Standard. Scale alongside VMSS instance count." + description = "Redis capacity (size index). For SKU=Standard / family=C, valid values are 0-6. Scale alongside VMSS instance count: 1 (1GB) handles 3-5 instances; 3 (6GB) handles 10-20+." type = number default = 1 } variable "redis_endpoint_override" { - description = "Host of an existing customer-managed Redis endpoint." + description = "Host of an existing customer-managed Redis endpoint. Pair with enable_managed_redis = false." type = string default = null } @@ -273,14 +282,14 @@ variable "redis_endpoint_override_tls" { variable "db_secret_expiration_hours" { - description = "Hours until the Key Vault DB-password secret expires. Default 8760 = one calendar year." + description = "Hours until the Key Vault DB-password secret expires. Default 8760 = one calendar year. Set on every apply via timeadd(timestamp(), ...) and then ignored on subsequent applies so a stale value doesn't show drift." type = number default = 8760 } variable "postgres_geo_redundant_backup_enabled" { - description = "Enable geo-redundant backup on Postgres Flexible Server. CKV_AZURE_136." + description = "Enable geo-redundant backup on the Postgres Flexible Server. Defaults to false; adds cross-region replication of backups for DR scenarios. CKV_AZURE_136." type = bool default = false } diff --git a/modules/sat-azure-ha/variables.tf b/modules/sat-azure-ha/variables.tf index 96ce28e..34cdb0c 100644 --- a/modules/sat-azure-ha/variables.tf +++ b/modules/sat-azure-ha/variables.tf @@ -43,13 +43,13 @@ variable "ssh_public_key" { } variable "key_vault_network_default_action" { - description = "Default action for the Key Vault network ACL. 'Allow' preserves the pre-network-ACL behavior; set 'Deny' once you've added the operator IP to key_vault_ip_rules and the Microsoft.KeyVault service endpoint on vm_subnet_id." + description = "Default action for the Key Vault network ACL. 'Allow' preserves the pre-network-ACL behavior (public endpoint open, RBAC-gated); set 'Deny' once you've added the operator IP to key_vault_ip_rules and the Microsoft.KeyVault service endpoint on vm_subnet_id. AzureServices bypass is always on so the VMs' managed identities can read secrets either way." type = string default = "Allow" } variable "key_vault_ip_rules" { - description = "IPv4 addresses or CIDRs allowed to reach the Key Vault data plane. Required only when key_vault_network_default_action = Deny and you don't have Private Link configured." + description = "IPv4 addresses or CIDRs allowed to reach the Key Vault data plane (typically the operator IP running terraform apply, or your bastion's egress NAT). Required only when default_action = Deny and you don't have Private Link configured." type = list(string) default = [] } @@ -75,7 +75,7 @@ variable "data_disk_size_gb" { } variable "enable_customer_managed_key" { - description = "Encrypt VM OS and data disks with a customer-managed key (RSA-4096 in the module's Key Vault, via a disk encryption set) instead of platform-managed keys." + description = "Encrypt VM OS and data disks with a customer-managed key (RSA-4096 in this module's Key Vault, via a disk encryption set) instead of platform-managed keys. Matches the single-vm tier's CMK option." type = bool default = false } @@ -127,7 +127,7 @@ variable "marketplace_image_version" { # ----- Patching and migration safety ----- variable "db_mode" { - description = "Database backend. 'flexible_server' (default) or 'vm' for self-managed Postgres on a Linux VM." + description = "Database backend. 'flexible_server' (default) provisions Azure Database for PostgreSQL Flexible Server — recommended for production. 'vm' provisions a third Linux VM with self-managed Postgres 16 for customers that must keep data plane on a VM." type = string default = "flexible_server" validation { @@ -155,19 +155,19 @@ variable "create_backup_storage_account" { } variable "backup_storage_account_name" { - description = "Name of an existing Storage Account to use." + description = "Name of an existing Storage Account to use. If null and create_backup_storage_account is true, the module names one." type = string default = null } variable "backup_storage_replication" { - description = "Replication type for the backup storage account." + description = "Replication type for the backup storage account. ZRS is the procurement-grade default." type = string default = "ZRS" } variable "backup_immutability_days" { - description = "Days the immutable blob policy keeps backup objects pinned." + description = "Days the immutable blob policy keeps backup objects pinned (unlocked mode so customers can extend later)." type = number default = 30 } @@ -185,45 +185,45 @@ variable "backup_blob_noncurrent_expiration_days" { } variable "enable_pre_patch_run_command" { - description = "Install an Azure Run Command document named RunPrePatchBackup." + description = "Install an Azure Run Command document named RunPrePatchBackup on the first SAT VM. Customers fire it from the Portal." type = bool default = true } variable "enable_application_gateway" { - description = "Front the LB topology with Azure Application Gateway (required for WAF parity with AWS ALB+WAF)." + description = "Front the LB topology with an Azure Application Gateway. Required if you want WAF parity with the AWS ALB+WAF story; the existing Standard LB is L4-only and cannot host WAF rules." type = bool default = false } variable "appgw_subnet_id" { - description = "Subnet for the Application Gateway. Required when enable_application_gateway = true." + description = "Subnet for the Application Gateway. Required when enable_application_gateway = true. Must be /24 or larger, in the same vnet as the VMs." type = string default = null } variable "appgw_tls_pfx_base64" { - description = "Base64-encoded PFX for the App Gateway HTTPS listener." + description = "Base64-encoded PFX bundle for the App Gateway HTTPS listener. Required when enable_application_gateway = true." type = string default = null sensitive = true } variable "appgw_tls_pfx_password" { - description = "Password for the PFX bundle." + description = "Password for the PFX bundle. Required when enable_application_gateway = true if the PFX is encrypted." type = string default = null sensitive = true } variable "appgw_backend_host_header" { - description = "Optional Host header App Gateway sends to the SAT backend." + description = "Optional Host header App Gateway sends to the SAT backend pool. Leave null to use the backend's IP." type = string default = null } variable "waf_policy_id" { - description = "Optional ID of an azurerm_web_application_firewall_policy to attach to the App Gateway." + description = "Optional ID of an azurerm_web_application_firewall_policy to attach to the App Gateway. HailBytes does not bundle a managed ruleset; most enterprises bring their own." type = string default = null } @@ -235,19 +235,19 @@ variable "alert_email" { } variable "refresh_rollback_5xx_count_threshold" { - description = "Backend 5xx response count threshold for the App Gateway alarm." + description = "Backend 5xx response count over 5 minutes that trips the patching alarm on the Application Gateway. Tune for site traffic; the Azure metric is a count, not a rate." type = number default = 10 } variable "schema_version_endpoint_path" { - description = "Path on the SAT API that returns the running schema version." + description = "Path on the SAT/ASM API that returns the running schema version." type = string default = "/api/instance/schema-version" } variable "enable_post_patch_run_command" { - description = "Install an Azure Run Command document named RunPostPatchVerify on each VM, mirroring the AWS sat-aws-ha aws_ssm_document.post_patch_verify." + description = "Install an Azure Run Command document named RunPostPatchVerify on each VM, mirroring the AWS aws_ssm_document.post_patch_verify in the SAT/ASM aws-ha modules. Customers fire it from the Portal after a Run Command-driven image swap." type = bool default = true } @@ -261,7 +261,7 @@ variable "enable_managed_redis" { } variable "redis_sku_name" { - description = "Redis SKU. Standard delivers a primary/replica pair across two zones; Premium adds persistence and explicit zone selection. Basic is single-node and NOT a valid HA option." + description = "Redis SKU. Standard delivers a primary/replica pair across two zones; Premium adds persistence and explicit zone selection. Basic is single-node and NOT a valid HA option (validated)." type = string default = "Standard" } @@ -273,7 +273,7 @@ variable "redis_family" { } variable "redis_capacity" { - description = "Redis capacity (size index). For SKU=Standard / family=C, valid values are 0 (250MB) through 6 (53GB). The procurement-friendly default is 1 (1GB)." + description = "Redis capacity (size index). For SKU=Standard / family=C, valid values are 0 (250MB) through 6 (53GB). cache.t4g.small-equivalent is 1 (1GB)." type = number default = 1 } @@ -285,25 +285,27 @@ variable "redis_endpoint_override" { } variable "redis_endpoint_override_port" { - type = number - default = 6380 + description = "Port on the customer-managed Redis endpoint. 6380 (TLS) is the Azure default. Ignored unless redis_endpoint_override is set." + type = number + default = 6380 } variable "redis_endpoint_override_tls" { - type = bool - default = true + description = "Whether the customer-managed Redis endpoint requires in-transit TLS. Ignored unless redis_endpoint_override is set." + type = bool + default = true } variable "db_secret_expiration_hours" { - description = "Hours until the Key Vault DB-password secret expires. Default 8760 = one calendar year." + description = "Hours until the Key Vault DB-password secret expires. Set on every apply via `timeadd(timestamp(), ...)` and then ignored on subsequent applies so a stale value doesn't show drift. Default 8760 = one calendar year — long enough that ops don't need to rotate weekly, short enough that the secret never lives unrotated past a year without operator attention." type = number default = 8760 } variable "postgres_geo_redundant_backup_enabled" { - description = "Enable geo-redundant backup on Postgres Flexible Server. CKV_AZURE_136." + description = "Enable geo-redundant backup on the Postgres Flexible Server. Defaults to false; adds cross-region replication of backups for DR scenarios. CKV_AZURE_136." type = bool default = false } diff --git a/modules/sat-azure-single/variables.tf b/modules/sat-azure-single/variables.tf index efda103..61fde4d 100644 --- a/modules/sat-azure-single/variables.tf +++ b/modules/sat-azure-single/variables.tf @@ -107,25 +107,25 @@ variable "marketplace_image_version" { # ----- Patching and migration safety ----- variable "create_backup_storage_account" { - description = "Provision a Storage Account + immutable container for pre-patch /api/instance/export bundles." + description = "Provision an Azure Storage Account + container (blob versioning + immutable WORM policy in unlocked mode + lifecycle to Cool at 30d and Archive at 90d) for pre-patch /api/instance/export bundles. The VM's system-assigned managed identity gets Storage Blob Data Contributor on the container only." type = bool default = true } variable "backup_storage_account_name" { - description = "Name of an existing Storage Account to use. If null and create_backup_storage_account is true, the module names one." + description = "Name of an existing Storage Account to use. If null and create_backup_storage_account is true, the module names one (lowercase, alphanumeric, max 24 chars). If non-null and create_backup_storage_account is false, the module only grants the managed identity blob writer perms on it." type = string default = null } variable "backup_storage_replication" { - description = "Replication type for the backup storage account. ZRS is the procurement-grade default." + description = "Replication type for the backup storage account. ZRS (zone-redundant) is the recommended default for procurement-grade durability. GRS adds cross-region replica." type = string default = "ZRS" } variable "backup_immutability_days" { - description = "Days the immutable blob policy keeps backup objects pinned (unlocked mode)." + description = "Days the immutable blob policy keeps backup objects pinned. Set in unlocked mode so customers can extend later through portal/CLI." type = number default = 30 } @@ -143,7 +143,7 @@ variable "backup_blob_noncurrent_expiration_days" { } variable "enable_pre_patch_run_command" { - description = "Install an Azure Run Command document named RunPrePatchBackup." + description = "Install an Azure Run Command document named RunPrePatchBackup. Customers can fire it from the Portal (VM -> Operations -> Run command) to take a pre-patch backup + managed-disk snapshot in one click. Disable if your AMI does not yet bundle ha-pre-patch-backup.sh." type = bool default = true }