From b6d2beaa3b254c7089759fb3bbdec1e43dd40225 Mon Sep 17 00:00:00 2001 From: Adam Moussa Date: Mon, 31 Aug 2026 12:49:30 -0400 Subject: [PATCH] fix(terraform): preserve live state during dev import --- terraform/live/dev/main.tf | 6 +- .../live/modules/environment-owned/main.tf | 279 +++++++++--------- .../modules/environment-owned/variables.tf | 15 + 3 files changed, 155 insertions(+), 145 deletions(-) diff --git a/terraform/live/dev/main.tf b/terraform/live/dev/main.tf index 3214b48..6391581 100644 --- a/terraform/live/dev/main.tf +++ b/terraform/live/dev/main.tf @@ -27,6 +27,7 @@ module "environment" { aws_region = local.aws_region environment = "dev" adoption_complete = false + manage_eb_settings = false eb_application_name = local.eb_application_name eb_environment_name = local.eb_environment_name eb_environment_id = local.eb_environment_id @@ -68,6 +69,10 @@ module "environment" { hosted_zone_id = "Z07671212N75U4YLPWZR8" api_domain = local.api_domain api_record_type = "A" + api_alias_target = { + name = "awseb--awseb-cmpb3ypfib53-1654918745.us-east-1.elb.amazonaws.com" + zone_id = "Z35SXDOTRQ7X7K" + } metadata_before_adoption = { runtime_role_description = "SHOC backend dev compute role (EB instance profile)" runtime_role_tags = { @@ -92,7 +97,6 @@ module "environment" { Project = "shoc-backend" } environment_tags = { - Name = "shoc-backend-dev" env = "dev" project = "shoc" } diff --git a/terraform/live/modules/environment-owned/main.tf b/terraform/live/modules/environment-owned/main.tf index 17c3b57..d4e4f27 100644 --- a/terraform/live/modules/environment-owned/main.tf +++ b/terraform/live/modules/environment-owned/main.tf @@ -327,6 +327,134 @@ resource "aws_iam_role_policy" "github_deploy" { } } +locals { + managed_eb_settings = concat( + [ + { + namespace = "aws:elasticbeanstalk:environment" + name = "EnvironmentType" + value = "LoadBalanced" + }, + { + namespace = "aws:elasticbeanstalk:environment" + name = "LoadBalancerType" + value = "application" + }, + { + namespace = "aws:elasticbeanstalk:environment" + name = "ServiceRole" + value = var.eb_service_role_name + }, + { + namespace = "aws:ec2:vpc" + name = "VPCId" + value = var.vpc_id + }, + { + namespace = "aws:ec2:vpc" + name = "Subnets" + value = join(",", sort(var.instance_subnet_ids)) + }, + { + namespace = "aws:ec2:vpc" + name = "ELBSubnets" + value = join(",", sort(var.load_balancer_subnet_ids)) + }, + { + namespace = "aws:ec2:vpc" + name = "ELBScheme" + value = "public" + }, + { + namespace = "aws:ec2:vpc" + name = "AssociatePublicIpAddress" + value = "true" + }, + { + namespace = "aws:autoscaling:launchconfiguration" + name = "IamInstanceProfile" + value = aws_iam_instance_profile.runtime.name + }, + { + namespace = "aws:autoscaling:launchconfiguration" + name = "InstanceType" + value = "t3.small" + }, + { + namespace = "aws:autoscaling:asg" + name = "MinSize" + value = "1" + }, + { + namespace = "aws:autoscaling:asg" + name = "MaxSize" + value = "1" + }, + { + namespace = "aws:elbv2:listener:443" + name = "Protocol" + value = "HTTPS" + }, + { + namespace = "aws:elbv2:listener:443" + name = "SSLCertificateArns" + value = var.shared_certificate_arn + }, + { + namespace = "aws:elasticbeanstalk:environment:process:default" + name = "HealthCheckPath" + value = "/" + }, + { + namespace = "aws:elasticbeanstalk:environment:process:default" + name = "MatcherHTTPCode" + value = "200-499" + }, + { + namespace = "aws:elasticbeanstalk:application:environment" + name = "ASPNETCORE_ENVIRONMENT" + value = "Production" + }, + { + namespace = "aws:elasticbeanstalk:application:environment" + name = "ASPNETCORE_URLS" + value = "http://0.0.0.0:5000" + }, + { + namespace = "aws:elasticbeanstalk:application:environment" + name = "WorkOrderWebhook__Enabled" + value = var.work_order_webhook_enabled ? "true" : "false" + }, + { + namespace = "aws:elasticbeanstalk:application:environment" + name = "WorkOrderWebhook__Region" + value = var.aws_region + }, + ], + var.instance_security_group_id == null ? [] : [ + { + namespace = "aws:autoscaling:launchconfiguration" + name = "SecurityGroups" + value = var.instance_security_group_id + }, + ], + [ + for key in sort(tolist(var.app_config_json_keys)) : { + namespace = "aws:elasticbeanstalk:application:environmentsecrets" + name = key + value = "${aws_secretsmanager_secret.app_config.arn}:${key}" + } + ], + var.webhook_secret_arn == null ? [] : [ + { + namespace = "aws:elasticbeanstalk:application:environment" + name = "WorkOrderWebhook__SecretId" + value = var.webhook_secret_arn + }, + ], + ) +} + resource "aws_elastic_beanstalk_environment" "this" { name = var.eb_environment_name application = var.eb_application_name @@ -334,150 +462,13 @@ resource "aws_elastic_beanstalk_environment" "this" { tier = "WebServer" cname_prefix = var.eb_environment_name - setting { - namespace = "aws:elasticbeanstalk:environment" - name = "EnvironmentType" - value = "LoadBalanced" - } - - setting { - namespace = "aws:elasticbeanstalk:environment" - name = "LoadBalancerType" - value = "application" - } - - setting { - namespace = "aws:elasticbeanstalk:environment" - name = "ServiceRole" - value = var.eb_service_role_name - } - - setting { - namespace = "aws:ec2:vpc" - name = "VPCId" - value = var.vpc_id - } - - setting { - namespace = "aws:ec2:vpc" - name = "Subnets" - value = join(",", sort(var.instance_subnet_ids)) - } - - setting { - namespace = "aws:ec2:vpc" - name = "ELBSubnets" - value = join(",", sort(var.load_balancer_subnet_ids)) - } - - setting { - namespace = "aws:ec2:vpc" - name = "ELBScheme" - value = "public" - } - - setting { - namespace = "aws:ec2:vpc" - name = "AssociatePublicIpAddress" - value = "true" - } - - setting { - namespace = "aws:autoscaling:launchconfiguration" - name = "IamInstanceProfile" - value = aws_iam_instance_profile.runtime.name - } - - setting { - namespace = "aws:autoscaling:launchconfiguration" - name = "InstanceType" - value = "t3.small" - } - dynamic "setting" { - for_each = var.instance_security_group_id == null ? [] : [var.instance_security_group_id] + for_each = var.manage_eb_settings ? local.managed_eb_settings : [] + content { - namespace = "aws:autoscaling:launchconfiguration" - name = "SecurityGroups" - value = setting.value - } - } - - setting { - namespace = "aws:autoscaling:asg" - name = "MinSize" - value = "1" - } - - setting { - namespace = "aws:autoscaling:asg" - name = "MaxSize" - value = "1" - } - - setting { - namespace = "aws:elbv2:listener:443" - name = "Protocol" - value = "HTTPS" - } - - setting { - namespace = "aws:elbv2:listener:443" - name = "SSLCertificateArns" - value = var.shared_certificate_arn - } - - setting { - namespace = "aws:elasticbeanstalk:environment:process:default" - name = "HealthCheckPath" - value = "/" - } - - setting { - namespace = "aws:elasticbeanstalk:environment:process:default" - name = "MatcherHTTPCode" - value = "200-499" - } - - dynamic "setting" { - for_each = var.app_config_json_keys - content { - namespace = "aws:elasticbeanstalk:application:environmentsecrets" - name = setting.value - value = "${aws_secretsmanager_secret.app_config.arn}:${setting.value}" - } - } - - setting { - namespace = "aws:elasticbeanstalk:application:environment" - name = "ASPNETCORE_ENVIRONMENT" - value = "Production" - } - - setting { - namespace = "aws:elasticbeanstalk:application:environment" - name = "ASPNETCORE_URLS" - value = "http://0.0.0.0:5000" - } - - setting { - namespace = "aws:elasticbeanstalk:application:environment" - name = "WorkOrderWebhook__Enabled" - value = var.work_order_webhook_enabled ? "true" : "false" - } - - setting { - namespace = "aws:elasticbeanstalk:application:environment" - name = "WorkOrderWebhook__Region" - value = var.aws_region - } - - dynamic "setting" { - for_each = var.webhook_secret_arn == null ? [] : [var.webhook_secret_arn] - content { - namespace = "aws:elasticbeanstalk:application:environment" - name = "WorkOrderWebhook__SecretId" - value = setting.value + namespace = setting.value.namespace + name = setting.value.name + value = setting.value.value } } @@ -499,8 +490,8 @@ resource "aws_route53_record" "api_alias" { type = "A" alias { - name = aws_elastic_beanstalk_environment.this.cname - zone_id = data.aws_elastic_beanstalk_hosted_zone.current.id + name = var.api_alias_target == null ? aws_elastic_beanstalk_environment.this.cname : var.api_alias_target.name + zone_id = var.api_alias_target == null ? data.aws_elastic_beanstalk_hosted_zone.current.id : var.api_alias_target.zone_id evaluate_target_health = true } diff --git a/terraform/live/modules/environment-owned/variables.tf b/terraform/live/modules/environment-owned/variables.tf index 9980d9d..49a6066 100644 --- a/terraform/live/modules/environment-owned/variables.tf +++ b/terraform/live/modules/environment-owned/variables.tf @@ -21,6 +21,12 @@ variable "adoption_complete" { default = false } +variable "manage_eb_settings" { + type = bool + description = "False omits managed Elastic Beanstalk settings during the import-only phase." + default = true +} + variable "eb_application_name" { type = string } @@ -205,6 +211,15 @@ variable "api_record_type" { } } +variable "api_alias_target" { + type = object({ + name = string + zone_id = string + }) + description = "Exact existing Route 53 alias target preserved during import. Null resolves the target from Elastic Beanstalk." + default = null +} + variable "metadata_before_adoption" { description = "Exact current metadata preserved while adoption_complete is false." type = object({