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 0000000..f7742ea Binary files /dev/null and b/.github/scripts/__pycache__/wrapper_drift_check.cpython-311.pyc differ 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 }