Skip to content

Support multiple public/private ALBs and NLBs per ECS cluster with per-service selection - #96

Open
devin-ai-integration[bot] wants to merge 4 commits into
mainfrom
devin/1786133993-multi-load-balancers
Open

Support multiple public/private ALBs and NLBs per ECS cluster with per-service selection#96
devin-ai-integration[bot] wants to merge 4 commits into
mainfrom
devin/1786133993-multi-load-balancers

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Summary

ECS clusters can now own arrays of load balancers instead of a single public/private ALB and NLB, and each ECS service selects which one it uses by name.

ecs_cluster Terraform (breaking, 2.0.0)

  • public_alb_* / private_alb_* / public_nlb_* / private_nlb_* singleton variables replaced by four object-array variables with per-item settings:
    variable "public_albs" {
      type = list(object({
        name, https_enabled, certificate_arns, ssl_policy, idle_timeout,
        ingress_cidr_blocks, ingress_ipv6_cidr_blocks, ingress_security_group_ids,
        access_logs_enabled, access_logs_bucket_arn, web_acl_arn
      }))
    }
    (NLB items: cross_zone_load_balancing_enabled, security_group_ids, access logs, Elastic IPs.)
  • LB modules use count = length(var.<array>); unnamed items get deterministic names (<name>-pub, <name>-pub-2, ...).
  • New outputs: public_albs / private_albs / public_nlbs / private_nlbs record lists (arn, dns_name, arn_suffix, security_group_id, listener ARNs, ...), plus *_by_name maps and *_options name lists. Pre-2.0 singleton outputs remain and return the first LB or null (used by e.g. the CloudFront and EC2 service definitions).
  • EC2 instance SG ingress now allows every public/private ALB SG.

Definitions

  • rvn-ecs-cluster (2.0.0): public_albs/private_albs/public_nlbs/private_nlbs become object_array inputs with per-item HTTPS/cert/WAF/logging (ALB) and cross-zone/EIP/logging (NLB) settings; UI links iterate the output arrays.
  • rvn-ecs-web / rvn-ecs-nlb (2.0.0): new public_alb_name / private_alb_name (and NLB equivalents) selector inputs under the cluster ref, populated via a new dynamic values source:
    $values:ravion/module_input_options?module_type=rvn-ecs-cluster&module_instance_id=<<module.input.cluster>>&input=public_albs&value_field=name
    Terraform mappings pick the selected LB with concat(filter(albs, #.name == selected_name), albs)[0].<field> — explicit selection wins, otherwise the first LB is used.
  • Shared partials pass the cluster's LB arrays through as mapped inputs (<<ref.stack.output.public_albs>> etc.) while keeping the first-LB singleton fields for rvn-ec2-service compatibility.

Requires flightcontrolhq/flightcontrol support for the ravion/module_input_options source and for rendering selector mapped inputs under a selected ref (companion PR).

Testing: tofu validate, new tests/load_balancers.tftest.hcl (23 runs, multi-LB coverage) — full suite has one pre-existing failure on main (ec2_spot_disabled_by_default asserts a nested module resource); ravion-modules tool tests updated and passing. Local dev publish was not run (no local dev API available in this environment).

Link to Devin session: https://app.devin.ai/sessions/42785cd5cb8f4b9f8824f73682f6350d
Requested by: @flybayer

Greptile Summary

This PR changes ECS clusters from singleton load balancers to named ALB/NLB arrays and adds per-service selection.

  • Adds per-item Terraform configuration, aggregate outputs, name maps, and compatibility singleton outputs.
  • Adds Ravion object-array inputs and dynamic selectors for ECS web and network services.
  • Extends EC2 ingress and tests for clusters containing multiple load balancers.

Confidence Score: 1/5

The PR is not safe to merge until public ALB ingress settings are preserved, stale selectors are rejected, and named load balancers receive stable Terraform identities.

The current definition can unintentionally expose public ALBs, explicit service selections can silently bind to another load balancer after cluster edits, and positional module addressing can replace unrelated load balancers during normal array changes.

Files Needing Attention: compute/ecs_cluster/rvn-ecs-cluster-definition.yml, compute/ecs_cluster/load_balancers.tf, compute/ecs_service/rvn-ecs-web-definition.yml, compute/ecs_service/rvn-ecs-nlb-definition.yml

Security Review

The public ALB module-definition path drops all ingress restrictions and consequently provisions the Terraform defaults of unrestricted IPv4 and IPv6 ingress. The public object-array form must expose and forward the CIDR and source-security-group fields before release.

Important Files Changed

Filename Overview
compute/ecs_cluster/rvn-ecs-cluster-definition.yml Adds load-balancer object arrays, but the public ALB mapping drops ingress controls and applies open Terraform defaults.
compute/ecs_cluster/load_balancers.tf Creates multiple load balancers positionally with count, making existing resources unstable under insertion, removal, or reordering.
compute/ecs_cluster/variables.tf Defines typed per-load-balancer objects, defaults, and uniqueness validations; its public ingress defaults expose the definition-mapping omission.
compute/ecs_cluster/outputs.tf Adds ordered records, name-keyed maps, selector options, and first-entry compatibility outputs.
compute/ecs_service/rvn-ecs-web-definition.yml Adds ALB selection, but stale explicit names silently fall back to the first ALB.
compute/ecs_service/rvn-ecs-nlb-definition.yml Adds equivalent NLB selection with the same stale-name fallback behavior.
partials/inputs/ecs-service-cluster-alb-mapped-inputs.yml Passes cluster ALB record arrays and selector fields into ECS web definitions.
partials/inputs/ecs-service-cluster-nlb-mapped-inputs.yml Passes cluster NLB record arrays and dynamic selector fields into ECS network services.
compute/ecs_cluster/tests/load_balancers.tftest.hcl Adds broad direct-Terraform multi-load-balancer coverage but does not exercise module-definition mappings or stale selectors.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  C["ECS cluster definition"] --> A["Named ALB/NLB arrays"]
  A --> T["Terraform cluster module"]
  T --> O["Load-balancer record outputs"]
  O --> S["Per-service name selector"]
  S --> W["ECS web/NLB attachment"]
  W --> L["Selected listener, security group, and metrics"]
Loading

Comments Outside Diff (1)

  1. compute/ecs_cluster/load_balancers.tf, line 15-17 (link)

    P1 Positional addresses destabilize named load balancers

    When an ALB or NLB entry is inserted, removed, or reordered, count shifts the Terraform addresses of subsequent entries even though their names are unchanged, causing Terraform to mutate or replace unrelated load balancers and potentially fail while deletion protection is enabled.

    Knowledge Base Used: ECS Cluster and ECS Service Modules

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: compute/ecs_cluster/load_balancers.tf
    Line: 15-17
    
    Comment:
    **Positional addresses destabilize named load balancers**
    
    When an ALB or NLB entry is inserted, removed, or reordered, `count` shifts the Terraform addresses of subsequent entries even though their names are unchanged, causing Terraform to mutate or replace unrelated load balancers and potentially fail while deletion protection is enabled.
    
    **Knowledge Base Used:** [ECS Cluster and ECS Service Modules](https://app.greptile.com/flightcontrol/-/custom-context/knowledge-base/flightcontrolhq/modules/-/docs/compute-ecs.md)
    
    ---
    
    For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.
Prompt To Fix All With AI
### Issue 1
compute/ecs_cluster/rvn-ecs-cluster-definition.yml:679-685
**Public ALB ingress controls are dropped**

When a cluster requires a public ALB restricted by CIDRs or source security groups, this mapping omits all three ingress fields, so Terraform applies `0.0.0.0/0`, `::/0`, and no source security groups, leaving the listener internet-accessible instead of enforcing the intended restrictions.

**How this was verified:** The public mapping omits the fields that the corresponding private mapping forwards, and the Terraform object defaults them to unrestricted IPv4 and IPv6 ingress.

### Issue 2
compute/ecs_service/rvn-ecs-web-definition.yml:344-351
**Stale selectors bind another load balancer**

When a selected load balancer is renamed or removed, `filter` returns an empty list and `concat(..., module.input.public_albs)[0]` silently selects the first remaining entry, causing the service listener, security group, and metric dimensions to point at a different ALB. The same fallback pattern also affects the NLB definition.

### Issue 3
compute/ecs_cluster/load_balancers.tf:15-17
**Positional addresses destabilize named load balancers**

When an ALB or NLB entry is inserted, removed, or reordered, `count` shifts the Terraform addresses of subsequent entries even though their names are unchanged, causing Terraform to mutate or replace unrelated load balancers and potentially fail while deletion protection is enabled.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Reviews (1): Last reviewed commit: "Support multiple public/private ALBs and..." | Re-trigger Greptile

Greptile also left 2 inline comments on this PR.

Context used:

…r-service selection

Co-Authored-By: brandon <brandon@flightcontrol.dev>
@flybayer flybayer self-assigned this Aug 7, 2026
@flybayer
flybayer self-requested a review August 7, 2026 20:20
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown

Ravion Module Publish Plan

Dry run only. No Ravion API mutations were made.

Module Current Version New Version Description
rvn-ec2-service 1.0.0 2.0.0 ECS cluster references now expose the cluster's load balancer arrays; the shared ALB fields default to the cluster's first public or private load balancer.
rvn-ecs-cluster 1.0.0 2.0.0 Support multiple public and private load balancers per cluster. Public/private ALBs and NLBs are now configured as arrays with per-load-balancer settings, and cluster outputs expose the full load balancer lists so services can pick one by name.
rvn-ecs-nlb 1.0.0 2.0.0 Support ECS clusters with multiple public and private network load balancers. Adds public/private NLB selectors under the cluster reference; the service uses the selected NLB's ARN and security group, and defaults to the cluster's first NLB when none is selected.
rvn-ecs-web 1.0.0 2.0.0 Support ECS clusters with multiple public and private load balancers. Adds public/private load balancer selectors under the cluster reference; the service uses the selected load balancer's listeners and security group, and defaults to the cluster's first load balancer when none is selected.

Diffs

rvn-ec2-service 1.0.0 -> 2.0.0

--- remote
+++ compiled
       - id: section_cluster_alb
         label: Cluster load balancers
         type: section
-      - default: <<ref.stack.output.public_alb_http_listener_arn>>
+      - collapsible: true
+        default: <<ref.stack.output.public_albs_by_name>>
+        description: Public load balancer records from the selected cluster keyed by name, including listener ARNs and security group IDs.
+        id: public_albs_by_name
+        label: Public load balancers by name
+        required: false
+        type: object
+      - collapsible: true
+        default: <<ref.stack.output.private_albs_by_name>>
+        description: Private load balancer records from the selected cluster keyed by name, including listener ARNs and security group IDs.
+        id: private_albs_by_name
+        label: Private load balancers by name
+        required: false
+        type: object
+      - collapsible: true
+        default: <<ref.stack.output.public_alb_http_listener_arn>>
+        description: HTTP listener ARN from the cluster's first public ALB.
         id: public_alb_http_listener_arn
         immutable: true
         label: Public ALB HTTP listener ARN
+        required: false
         type: string
       - collapsible: true
         default: <<ref.stack.output.public_alb_https_listener_arn>>
-        description: HTTPS listener ARN from the selected public ALB. Ravion uses it when present, otherwise falls back to HTTP.
+        description: HTTPS listener ARN from the cluster's first public ALB. Ravion uses it when present, otherwise falls back to HTTP.
         id: public_alb_https_listener_arn
         immutable: true
         label: Public ALB HTTPS listener ARN
         required: false
         type: string
-      - default: <<ref.stack.output.public_alb_security_group_id>>
+      - collapsible: true
+        default: <<ref.stack.output.public_alb_security_group_id>>
+        description: Security group ID of the cluster's first public ALB.
         id: public_alb_security_group_id
         immutable: true
         label: Public ALB security group ID
+        required: false
         type: string
-      - default: <<ref.stack.output.private_alb_http_listener_arn>>
+      - collapsible: true
+        default: <<ref.stack.output.public_alb_arn_suffix>>
+        description: ARN suffix of the cluster's first public ALB for CloudWatch metrics.
+        id: public_alb_arn_suffix
+        immutable: true
+        label: Public ALB ARN suffix
+        required: false
+        type: string
+      - collapsible: true
+        default: <<ref.stack.output.private_alb_http_listener_arn>>
+        description: HTTP listener ARN from the cluster's first private ALB.
         id: private_alb_http_listener_arn
         immutable: true
         label: Private ALB HTTP listener ARN
+        required: false
         type: string
       - collapsible: true
         default: <<ref.stack.output.private_alb_https_listener_arn>>
-        description: HTTPS listener ARN from the selected private ALB. Ravion uses it when present, otherwise falls back to HTTP.
+        description: HTTPS listener ARN from the cluster's first private ALB. Ravion uses it when present, otherwise falls back to HTTP.
         id: private_alb_https_listener_arn
         immutable: true
         label: Private ALB HTTPS listener ARN
         required: false
         type: string
-      - default: <<ref.stack.output.private_alb_security_group_id>>
+      - collapsible: true
+        default: <<ref.stack.output.private_alb_security_group_id>>
+        description: Security group ID of the cluster's first private ALB.
         id: private_alb_security_group_id
         immutable: true
         label: Private ALB security group ID
-        type: string
-      - default: <<ref.stack.output.public_alb_arn_suffix>>
-        id: public_alb_arn_suffix
-        immutable: true
-        label: Public ALB ARN suffix
+        required: false
         type: string
-      - default: <<ref.stack.output.private_alb_arn_suffix>>
+      - collapsible: true
+        default: <<ref.stack.output.private_alb_arn_suffix>>
+        description: ARN suffix of the cluster's first private ALB for CloudWatch metrics.
         id: private_alb_arn_suffix
         immutable: true
         label: Private ALB ARN suffix
+        required: false
         type: string
     required: true
     show_when:
@@
 
   Ravion provisions the launch template, Auto Scaling Group, instance role and security group, SSM deploy document, app log group, and optional target group and listener rule. Web services can attach to a standalone Application Load Balancer or reuse the public or private ALB from an ECS cluster. The selected network and load balancer must use the same AWS account, region, and VPC. The Auto Scaling Group can still terminate instances because of scaling, health failures, or operator action. Root and data EBS volumes survive releases, but not instance termination.
 
-  Terraform source: [flightcontrolhq/modules/compute/ec2_service](https://github.com/flightcontrolhq/modules/tree/rvn-ec2-service@1.0.0/compute/ec2_service)
+  Terraform source: [flightcontrolhq/modules/compute/ec2_service](https://github.com/flightcontrolhq/modules/tree/rvn-ec2-service@2.0.0/compute/ec2_service)
 
   ## Use cases
 
@@
         base_path: compute/ec2_service
         branch: main
         execution_environment_id: << module.input.execution_environment_id >>
-        ref: rvn-ec2-service@1.0.0
+        ref: rvn-ec2-service@2.0.0
         repo: https://github.com/flightcontrolhq/modules
         stack_id: <<stack.id>>
         terraform_variables:

rvn-ecs-cluster 1.0.0 -> 2.0.0

--- remote
+++ compiled
         not: ""
       ec2_managed_scaling_enabled: true
     type: number
-  - description: Creates an internet-facing Application Load Balancer for services that should receive traffic from the public internet, such as websites, public APIs, and webhook endpoints.
-    id: section_public_alb
-    label: Public application load balancer
+  - description: Internet-facing Application Load Balancers for services that should receive traffic from the public internet, such as websites, public APIs, and webhook endpoints.
+    id: section_public_albs
+    label: Public application load balancers
     type: section
-  - default: true
-    id: public_alb_enabled
-    label: Public load balancer
-    type: boolean
-  - default: true
-    description: Enable HTTPS listener on the public ALB.
-    id: public_alb_https_enabled
-    label: HTTPS
-    show_when:
-      public_alb_enabled: true
-    type: boolean
-  - description: Primary ACM certificate module for public ALB HTTPS.
-    id: public_alb_certificate
-    label: Certificate
-    mapped_inputs:
+  - default:
+      - https_enabled: true
+        name: <<project.given_id>>-<<environment.given_id>>-pub
+    description: Each entry creates one internet-facing Application Load Balancer. Services on this cluster choose a load balancer by name.
+    id: public_albs
+    item_inputs:
+      - description: Unique load balancer name.
+        id: name
+        label: Name
+        patterns:
+          - message: The name must be 1-32 characters, contain only letters, numbers, and hyphens, and start and end with a letter or number.
+            pattern: ^[a-zA-Z0-9]([a-zA-Z0-9-]{0,30}[a-zA-Z0-9])?$
+        required: true
+        type: string
+      - default: true
+        description: Enable an HTTPS listener on this load balancer.
+        id: https_enabled
+        label: HTTPS
+        type: boolean
+      - description: Primary ACM certificate module for HTTPS.
+        id: certificate
+        label: Certificate
+        mapped_inputs:
+          - add_button_label: Add certificate ARN
+            default:
+              - <<ref.stack.output.certificate_arn>>
+            description: ACM certificate ARNs resolved from the selected module. The first ARN is the default certificate.
+            id: certificate_arns
+            label: Certificate ARNs
+            placeholder: arn:aws:acm:...
+            required: true
+            type: string_array
+        required: true
+        show_when:
+          https_enabled: true
+        type: $ref:rvn-acm-certificate
       - add_button_label: Add certificate ARN
-        default:
-          - <<ref.stack.output.certificate_arn>>
-        description: ACM certificate ARNs resolved from the selected module. The first ARN is the default certificate.
-        id: public_alb_certificate_arns
-        label: Certificate ARNs
+        collapsible: true
+        default: []
+        description: Additional ACM certificate ARNs attached to the HTTPS listener for SNI.
+        id: additional_certificate_arns
+        label: Additional certificate ARNs
         placeholder: arn:aws:acm:...
-        required: true
+        show_when:
+          https_enabled: true
         type: string_array
-    required: true
-    show_when:
-      public_alb_enabled: true
-      public_alb_https_enabled: true
-    type: $ref:rvn-acm-certificate
-  - add_button_label: Add certificate ARN
-    collapsible: true
-    default: []
-    description: Additional ACM certificate ARNs attached to the public ALB HTTPS listener for SNI.
-    id: public_alb_additional_certificate_arns
-    label: Additional certificate ARNs
-    placeholder: arn:aws:acm:...
-    show_when:
-      public_alb_enabled: true
-      public_alb_https_enabled: true
-    type: string_array
-  - collapsible: true
-    description: SSL policy for public ALB HTTPS.
-    id: public_alb_ssl_policy
-    label: SSL policy
-    placeholder: ELBSecurityPolicy-TLS13-1-2-2021-06
-    show_when:
-      public_alb_enabled: true
-      public_alb_https_enabled: true
-    type: string
-  - collapsible: true
-    description: Idle timeout for the public ALB.
-    id: public_alb_idle_timeout
-    label: Idle timeout (seconds)
-    max: 4000
-    min: 1
-    placeholder: "60"
-    show_when:
-      public_alb_enabled: true
-    type: number
-  - collapsible: true
-    description: WAFv2 Web ACL ARN for the public ALB.
-    id: public_alb_web_acl_arn
-    label: WAF web ACL ARN
-    placeholder: arn:aws:wafv2:...
-    show_when:
-      public_alb_enabled: true
-    type: string
-  - collapsible: true
-    default: false
-    description: Enable public ALB access logging.
-    id: public_alb_access_logs_enabled
-    label: Access logs
-    show_when:
-      public_alb_enabled: true
-    type: boolean
-  - collapsible: true
-    description: Existing S3 bucket ARN for public ALB access logs.
-    id: public_alb_access_logs_bucket_arn
-    label: Access logs bucket ARN
-    placeholder: arn:aws:s3:::my-bucket
-    show_when:
-      public_alb_access_logs_enabled: true
-      public_alb_enabled: true
-    type: string
-  - description: Creates an internal Application Load Balancer for services that should only be reachable inside the VPC or private network, such as internal APIs, admin tools, and service-to-service traffic.
-    id: section_private_alb
-    label: Private application load balancer
+      - collapsible: true
+        description: SSL policy for the HTTPS listener.
+        id: ssl_policy
+        label: SSL policy
+        placeholder: ELBSecurityPolicy-TLS13-1-2-2021-06
+        show_when:
+          https_enabled: true
+        type: string
+      - add_button_label: Add CIDR block
+        collapsible: true
+        description: Allowed IPv4 CIDRs. Defaults to allowing all IPv4 traffic.
+        id: ingress_cidr_blocks
+        label: Ingress CIDRs
+        placeholder: 0.0.0.0/0
+        type: string_array
+      - add_button_label: Add IPv6 CIDR block
+        collapsible: true
+        description: Allowed IPv6 CIDRs. Defaults to allowing all IPv6 traffic.
+        id: ingress_ipv6_cidr_blocks
+        label: Ingress IPv6 CIDRs
+        placeholder: ::/0
+        type: string_array
+      - add_button_label: Add security group
+        collapsible: true
+        description: Security groups whose members can access this load balancer. Useful for sources without static CIDRs, such as CloudFront VPC origins.
+        id: ingress_security_group_ids
+        label: Ingress security groups
+        placeholder: sg-...
+        type: string_array
+      - collapsible: true
+        description: Idle timeout for this load balancer.
+        id: idle_timeout
+        label: Idle timeout (seconds)
+        max: 4000
+        min: 1
+        placeholder: "60"
+        type: number
+      - collapsible: true
+        description: WAFv2 Web ACL ARN for this load balancer.
+        id: web_acl_arn
+        label: WAF web ACL ARN
+        placeholder: arn:aws:wafv2:...
+        type: string
+      - collapsible: true
+        default: false
+        description: Enable access logging for this load balancer.
+        id: access_logs_enabled
+        label: Access logs
+        type: boolean
+      - collapsible: true
+        description: Existing S3 bucket ARN for access logs.
+        id: access_logs_bucket_arn
+        label: Access logs bucket ARN
+        placeholder: arn:aws:s3:::my-bucket
+        show_when:
+          access_logs_enabled: true
+        type: string
+    item_label: Public load balancer
+    item_title:
+      template: "{name}"
+    label: Public load balancers
+    type: object_array
+  - description: Internal Application Load Balancers for services that should only be reachable inside the VPC or private network, such as internal APIs, admin tools, and service-to-service traffic.
+    id: section_private_albs
+    label: Private application load balancers
     type: section
-  - default: false
-    id: private_alb_enabled
-    label: Private load balancer
-    type: boolean
-  - default: false
-    description: Enable HTTPS listener on the private ALB.
-    id: private_alb_https_enabled
-    label: HTTPS
-    show_when:
-      private_alb_enabled: true
-    type: boolean
-  - description: Primary ACM certificate module for private ALB HTTPS.
-    id: private_alb_certificate
-    label: Certificate
-    mapped_inputs:
+  - default: []
+    description: Each entry creates one internal Application Load Balancer. Services on this cluster choose a load balancer by name.
+    id: private_albs
+    item_inputs:
+      - description: Unique load balancer name.
+        id: name
+        label: Name
+        patterns:
+          - message: The name must be 1-32 characters, contain only letters, numbers, and hyphens, and start and end with a letter or number.
+            pattern: ^[a-zA-Z0-9]([a-zA-Z0-9-]{0,30}[a-zA-Z0-9])?$
+        required: true
+        type: string
+      - default: false
+        description: Enable an HTTPS listener on this load balancer.
+        id: https_enabled
+        label: HTTPS
+        type: boolean
+      - description: Primary ACM certificate module for HTTPS.
+        id: certificate
+        label: Certificate
+        mapped_inputs:
+          - add_button_label: Add certificate ARN
+            default:
+              - <<ref.stack.output.certificate_arn>>
+            description: ACM certificate ARNs resolved from the selected module. The first ARN is the default certificate.
+            id: certificate_arns
+            label: Certificate ARNs
+            placeholder: arn:aws:acm:...
+            required: true
+            type: string_array
+        required: true
+        show_when:
+          https_enabled: true
+        type: $ref:rvn-acm-certificate
       - add_button_label: Add certificate ARN
-        default:
-          - <<ref.stack.output.certificate_arn>>
-        description: ACM certificate ARNs resolved from the selected module. The first ARN is the default certificate.
-        id: private_alb_certificate_arns
-        label: Certificate ARNs
+        collapsible: true
+        default: []
+        description: Additional ACM certificate ARNs attached to the HTTPS listener for SNI.
+        id: additional_certificate_arns
+        label: Additional certificate ARNs
         placeholder: arn:aws:acm:...
-        required: true
+        show_when:
+          https_enabled: true
         type: string_array
-    required: true
-    show_when:
-      private_alb_enabled: true
-      private_alb_https_enabled: true
-    type: $ref:rvn-acm-certificate
-  - add_button_label: Add certificate ARN
-    collapsible: true
-    default: []
-    description: Additional ACM certificate ARNs attached to the private ALB HTTPS listener for SNI.
-    id: private_alb_additional_certificate_arns
-    label: Additional certificate ARNs
-    placeholder: arn:aws:acm:...
-    show_when:
-      private_alb_enabled: true
-      private_alb_https_enabled: true
-    type: string_array
-  - collapsible: true
-    description: SSL policy for private ALB HTTPS.
-    id: private_alb_ssl_policy
-    label: SSL policy
-    placeholder: ELBSecurityPolicy-TLS13-1-2-2021-06
-    show_when:
-      private_alb_enabled: true
-      private_alb_https_enabled: true
-    type: string
-  - add_button_label: Add CIDR block
-    collapsible: true
-    description: Allowed IPv4 CIDRs for private ALB access. Terraform defaults to RFC1918 private ranges.
-    id: private_alb_ingress_cidr_blocks
-    label: Ingress CIDRs
-    placeholder: 10.0.0.0/8
-    show_when:
-      private_alb_enabled: true
-    type: string_array
-  - add_button_label: Add IPv6 CIDR block
-    collapsible: true
-    description: Allowed IPv6 CIDRs for private ALB access. Defaults to no IPv6 ingress.
-    id: private_alb_ingress_ipv6_cidr_blocks
-    label: Ingress IPv6 CIDRs
-    placeholder: fd00::/8
-    show_when:
-      pri
... diff truncated ...

rvn-ecs-nlb 1.0.0 -> 2.0.0

--- remote
+++ compiled
       - id: section_cluster_nlb
         label: Cluster network load balancers
         type: section
-      - default: <<ref.stack.output.public_nlb_arn>>
+      - description: Which of the cluster's public network load balancers this service uses. Defaults to the first one when unset; a name that no longer exists on the cluster fails the deployment instead of silently using another load balancer.
+        id: public_nlb_name
+        label: Public NLB
+        required: false
+        show_when:
+          public_nlb_service_enabled: true
+        type: string
+        values: $values:ravion/module_input_options?module_type=rvn-ecs-cluster&module_instance_id=<<module.input.cluster>>&input=public_nlbs&value_field=name
+      - description: Which of the cluster's private network load balancers this service uses. Defaults to the first one when unset; a name that no longer exists on the cluster fails the deployment instead of silently using another load balancer.
+        id: private_nlb_name
+        label: Private NLB
+        required: false
+        show_when:
+          public_nlb_service_enabled: false
+        type: string
+        values: $values:ravion/module_input_options?module_type=rvn-ecs-cluster&module_instance_id=<<module.input.cluster>>&input=private_nlbs&value_field=name
+      - collapsible: true
+        default: <<ref.stack.output.public_nlbs_by_name>>
+        description: Public network load balancer records from the selected cluster keyed by name, including ARNs and security group IDs.
+        id: public_nlbs_by_name
+        label: Public NLBs by name
+        required: false
+        type: object
+      - collapsible: true
+        default: <<ref.stack.output.private_nlbs_by_name>>
+        description: Private network load balancer records from the selected cluster keyed by name, including ARNs and security group IDs.
+        id: private_nlbs_by_name
+        label: Private NLBs by name
+        required: false
+        type: object
+      - collapsible: true
+        default: <<ref.stack.output.public_nlb_arn>>
+        description: ARN of the cluster's first public NLB.
         id: public_nlb_arn
         immutable: true
         label: Public NLB ARN
-        type: string
-      - default: <<ref.stack.output.public_nlb_arn_suffix>>
-        id: public_nlb_arn_suffix
-        immutable: true
-        label: Public NLB ARN suffix
+        required: false
         type: string
-      - default: <<ref.stack.output.public_nlb_security_group_id>>
+      - collapsible: true
+        default: <<ref.stack.output.public_nlb_security_group_id>>
+        description: Security group ID of the cluster's first public NLB.
         id: public_nlb_security_group_id
         immutable: true
         label: Public NLB security group ID
+        required: false
         type: string
-      - default: <<ref.stack.output.private_nlb_arn>>
-        id: private_nlb_arn
+      - collapsible: true
+        default: <<ref.stack.output.public_nlb_arn_suffix>>
+        description: ARN suffix of the cluster's first public NLB for CloudWatch metrics.
+        id: public_nlb_arn_suffix
         immutable: true
-        label: Private NLB ARN
+        label: Public NLB ARN suffix
+        required: false
         type: string
-      - default: <<ref.stack.output.private_nlb_arn_suffix>>
-        id: private_nlb_arn_suffix
+      - collapsible: true
+        default: <<ref.stack.output.private_nlb_arn>>
+        description: ARN of the cluster's first private NLB.
+        id: private_nlb_arn
         immutable: true
-        label: Private NLB ARN suffix
+        label: Private NLB ARN
+        required: false
         type: string
-      - default: <<ref.stack.output.private_nlb_security_group_id>>
+      - collapsible: true
+        default: <<ref.stack.output.private_nlb_security_group_id>>
+        description: Security group ID of the cluster's first private NLB.
         id: private_nlb_security_group_id
         immutable: true
         label: Private NLB security group ID
+        required: false
         type: string
+      - collapsible: true
+        default: <<ref.stack.output.private_nlb_arn_suffix>>
+        description: ARN suffix of the cluster's first private NLB for CloudWatch metrics.
+        id: private_nlb_arn_suffix
+        immutable: true
+        label: Private NLB ARN suffix
+        required: false
+        type: string
     required: true
     type: $ref:rvn-ecs-cluster
   - id: section_service
@@
 
   The module is intentionally focused on Layer 4 services behind a Network Load Balancer. Use ECS Web Service for HTTP host and path routing through an Application Load Balancer.
 
-  Terraform source: [flightcontrolhq/modules/compute/ecs_service](https://github.com/flightcontrolhq/modules/tree/rvn-ecs-nlb@1.0.0/compute/ecs_service)
+  Terraform source: [flightcontrolhq/modules/compute/ecs_service](https://github.com/flightcontrolhq/modules/tree/rvn-ecs-nlb@2.0.0/compute/ecs_service)
 
   ## Use cases
 
@@
         base_path: compute/ecs_service
         branch: main
         execution_environment_id: << module.input.execution_environment_id >>
-        ref: rvn-ecs-nlb@1.0.0
+        ref: rvn-ecs-nlb@2.0.0
         repo: https://github.com/flightcontrolhq/modules
         stack_id: <<stack.id>>
         terraform_variables:
@@
           load_balancer_attachment:
             container_port: << module.input.listeners[0].container_port >>
             enabled: true
-            nlb_listeners: '<< map(module.input.listeners, {"nlb_arn": (module.input.public_nlb_service_enabled ? module.input.public_nlb_arn : module.input.private_nlb_arn), "port": #.listener_port, "protocol": #.listener_protocol, "container_port": #.container_port, "target_protocol": (#.listener_protocol == "TLS" ? #.tls_target_protocol : #.listener_protocol), "certificate_arn": (#.listener_protocol == "TLS" ? #.tls_certificate_arn : nil), "ssl_policy": (#.listener_protocol == "TLS" ? #.tls_ssl_policy : nil), "alpn_policy": (#.listener_protocol == "TLS" && #.tls_alpn_policy != "None" ? #.tls_alpn_policy : nil)}) >>'
+            nlb_listeners: '<< map(module.input.listeners, {"nlb_arn": (module.input.public_nlb_service_enabled ? ((module.input.public_nlb_name || "") == "" ? module.input.public_nlb_arn : get(get(module.input.public_nlbs_by_name, module.input.public_nlb_name), "arn")) : ((module.input.private_nlb_name || "") == "" ? module.input.private_nlb_arn : get(get(module.input.private_nlbs_by_name, module.input.private_nlb_name), "arn"))), "port": #.listener_port, "protocol": #.listener_protocol, "container_port": #.container_port, "target_protocol": (#.listener_protocol == "TLS" ? #.tls_target_protocol : #.listener_protocol), "certificate_arn": (#.listener_protocol == "TLS" ? #.tls_certificate_arn : nil), "ssl_policy": (#.listener_protocol == "TLS" ? #.tls_ssl_policy : nil), "alpn_policy": (#.listener_protocol == "TLS" && #.tls_alpn_policy != "None" ? #.tls_alpn_policy : nil)}) >>'
             target_group:
               deregistration_delay: << module.input.deregistration_delay >>
               health_check:
@@
                 type: source_ip
               target_type: ip
           load_balancer_ingress_cidr_blocks: "<< module.input.public_nlb_service_enabled ? module.input.load_balancer_ingress_cidr_blocks : module.input.private_load_balancer_ingress_cidr_blocks >>"
-          load_balancer_security_group_id: "<< module.input.public_nlb_service_enabled ? module.input.public_nlb_security_group_id : module.input.private_nlb_security_group_id >>"
+          load_balancer_security_group_id: '<< module.input.public_nlb_service_enabled ? ((module.input.public_nlb_name || "") == "" ? module.input.public_nlb_security_group_id : get(get(module.input.public_nlbs_by_name, module.input.public_nlb_name), "security_group_id")) : ((module.input.private_nlb_name || "") == "" ? module.input.private_nlb_security_group_id : get(get(module.input.private_nlbs_by_name, module.input.private_nlb_name), "security_group_id")) >>'
           name: << module.input.name >>
           network_mode: awsvpc
           new_deployment_forcing_enabled: << module.input.new_deployment_forcing_enabled >>
@@
       source:
         aws_account_id: << module.input.aws_account_id >>
         dimensions:
-          LoadBalancer: "<< module.input.public_nlb_service_enabled ? module.input.public_nlb_arn_suffix : module.input.private_nlb_arn_suffix >>"
+          LoadBalancer: '<< module.input.public_nlb_service_enabled ? ((module.input.public_nlb_name || "") == "" ? module.input.public_nlb_arn_suffix : get(get(module.input.public_nlbs_by_name, module.input.public_nlb_name), "arn_suffix")) : ((module.input.private_nlb_name || "") == "" ? module.input.private_nlb_arn_suffix : get(get(module.input.private_nlbs_by_name, module.input.private_nlb_name), "arn_suffix")) >>'
           TargetGroup: << stack.output.target_group_arn_suffix >>
         name: HealthyHostCount
         namespace: AWS/NetworkELB
@@
       source:
         aws_account_id: << module.input.aws_account_id >>
         dimensions:
-          LoadBalancer: "<< module.input.public_nlb_service_enabled ? module.input.public_nlb_arn_suffix : module.input.private_nlb_arn_suffix >>"
+          LoadBalancer: '<< module.input.public_nlb_service_enabled ? ((module.input.public_nlb_name || "") == "" ? module.input.public_nlb_arn_suffix : get(get(module.input.public_nlbs_by_name, module.input.public_nlb_name), "arn_suffix")) : ((module.input.private_nlb_name || "") == "" ? module.input.private_nlb_arn_suffix : get(get(module.input.private_nlbs_by_name, module.input.private_nlb_name), "arn_suffix")) >>'
           TargetGroup: << stack.output.target_group_arn_suffix >>
         name: UnHealthyHostCount
         namespace: AWS/NetworkELB

rvn-ecs-web 1.0.0 -> 2.0.0

--- remote
+++ compiled
       - id: section_cluster_alb
         label: Cluster load balancers
         type: section
-      - default: <<ref.stack.output.public_alb_http_listener_arn>>
+      - collapsible: true
+        default: <<ref.stack.output.public_albs_by_name>>
+        description: Public load balancer records from the selected cluster keyed by name, including listener ARNs and security group IDs.
+        id: public_albs_by_name
+        label: Public load balancers by name
+        required: false
+        type: object
+      - collapsible: true
+        default: <<ref.stack.output.private_albs_by_name>>
+        description: Private load balancer records from the selected cluster keyed by name, including listener ARNs and security group IDs.
+        id: private_albs_by_name
+        label: Private load balancers by name
+        required: false
+        type: object
+      - collapsible: true
+        default: <<ref.stack.output.public_alb_http_listener_arn>>
+        description: HTTP listener ARN from the cluster's first public ALB.
         id: public_alb_http_listener_arn
         immutable: true
         label: Public ALB HTTP listener ARN
+        required: false
         type: string
       - collapsible: true
         default: <<ref.stack.output.public_alb_https_listener_arn>>
-        description: HTTPS listener ARN from the selected public ALB. Ravion uses it when present, otherwise falls back to HTTP.
+        description: HTTPS listener ARN from the cluster's first public ALB. Ravion uses it when present, otherwise falls back to HTTP.
         id: public_alb_https_listener_arn
         immutable: true
         label: Public ALB HTTPS listener ARN
         required: false
         type: string
-      - default: <<ref.stack.output.public_alb_security_group_id>>
+      - collapsible: true
+        default: <<ref.stack.output.public_alb_security_group_id>>
+        description: Security group ID of the cluster's first public ALB.
         id: public_alb_security_group_id
         immutable: true
         label: Public ALB security group ID
+        required: false
         type: string
-      - default: <<ref.stack.output.private_alb_http_listener_arn>>
+      - collapsible: true
+        default: <<ref.stack.output.public_alb_arn_suffix>>
+        description: ARN suffix of the cluster's first public ALB for CloudWatch metrics.
+        id: public_alb_arn_suffix
+        immutable: true
+        label: Public ALB ARN suffix
+        required: false
+        type: string
+      - collapsible: true
+        default: <<ref.stack.output.private_alb_http_listener_arn>>
+        description: HTTP listener ARN from the cluster's first private ALB.
         id: private_alb_http_listener_arn
         immutable: true
         label: Private ALB HTTP listener ARN
+        required: false
         type: string
       - collapsible: true
         default: <<ref.stack.output.private_alb_https_listener_arn>>
-        description: HTTPS listener ARN from the selected private ALB. Ravion uses it when present, otherwise falls back to HTTP.
+        description: HTTPS listener ARN from the cluster's first private ALB. Ravion uses it when present, otherwise falls back to HTTP.
         id: private_alb_https_listener_arn
         immutable: true
         label: Private ALB HTTPS listener ARN
         required: false
         type: string
-      - default: <<ref.stack.output.private_alb_security_group_id>>
+      - collapsible: true
+        default: <<ref.stack.output.private_alb_security_group_id>>
+        description: Security group ID of the cluster's first private ALB.
         id: private_alb_security_group_id
         immutable: true
         label: Private ALB security group ID
-        type: string
-      - default: <<ref.stack.output.public_alb_arn_suffix>>
-        id: public_alb_arn_suffix
-        immutable: true
-        label: Public ALB ARN suffix
+        required: false
         type: string
-      - default: <<ref.stack.output.private_alb_arn_suffix>>
+      - collapsible: true
+        default: <<ref.stack.output.private_alb_arn_suffix>>
+        description: ARN suffix of the cluster's first private ALB for CloudWatch metrics.
         id: private_alb_arn_suffix
         immutable: true
         label: Private ALB ARN suffix
+        required: false
         type: string
+      - description: Which of the cluster's public load balancers this service uses. Defaults to the first one when unset; a name that no longer exists on the cluster fails the deployment instead of silently using another load balancer.
+        id: public_alb_name
+        label: Public load balancer
+        required: false
+        show_when:
+          public_web_service_enabled: true
+        type: string
+        values: $values:ravion/module_input_options?module_type=rvn-ecs-cluster&module_instance_id=<<module.input.cluster>>&input=public_albs&value_field=name
+      - description: Which of the cluster's private load balancers this service uses. Defaults to the first one when unset; a name that no longer exists on the cluster fails the deployment instead of silently using another load balancer.
+        id: private_alb_name
+        label: Private load balancer
+        required: false
+        show_when:
+          public_web_service_enabled: false
+        type: string
+        values: $values:ravion/module_input_options?module_type=rvn-ecs-cluster&module_instance_id=<<module.input.cluster>>&input=private_albs&value_field=name
     required: true
     type: $ref:rvn-ecs-cluster
   - id: section_service
@@
 
   The module is intentionally focused on web services behind an Application Load Balancer. It uses the selected ECS cluster to inherit AWS account, region, VPC, subnets, capacity providers, load balancer listeners, and load balancer security groups.
 
-  Terraform source: [flightcontrolhq/modules/compute/ecs_service](https://github.com/flightcontrolhq/modules/tree/rvn-ecs-web@1.0.0/compute/ecs_service)
+  Terraform source: [flightcontrolhq/modules/compute/ecs_service](https://github.com/flightcontrolhq/modules/tree/rvn-ecs-web@2.0.0/compute/ecs_service)
 
   ## Use cases
 
@@
         base_path: compute/ecs_service
         branch: main
         execution_environment_id: << module.input.execution_environment_id >>
-        ref: rvn-ecs-web@1.0.0
+        ref: rvn-ecs-web@2.0.0
         repo: https://github.com/flightcontrolhq/modules
         stack_id: <<stack.id>>
         terraform_variables:
@@
               - conditions:
                   - '...<< module.input.host_header_values != nil && module.input.host_header_values != [] ? [{type: "host-header", values: module.input.host_header_values}] : [] >>'
                   - '...<< module.input.path_pattern_values != nil && module.input.path_pattern_values != [] ? [{type: "path-pattern", values: module.input.path_pattern_values}] : module.input.host_header_values != nil && module.input.host_header_values != [] ? [] : [{type: "path-pattern", values: ["/*"]}] >>'
-                listener_arn: "<< module.input.public_web_service_enabled ? (module.input.public_alb_https_listener_arn || module.input.public_alb_http_listener_arn) : (module.input.private_alb_https_listener_arn || module.input.private_alb_http_listener_arn) >>"
+                listener_arn: '<< module.input.public_web_service_enabled ? ((module.input.public_alb_name || "") == "" ? (module.input.public_alb_https_listener_arn || module.input.public_alb_http_listener_arn) : (get(get(module.input.public_albs_by_name, module.input.public_alb_name), "https_listener_arn") || get(get(module.input.public_albs_by_name, module.input.public_alb_name), "http_listener_arn"))) : ((module.input.private_alb_name || "") == "" ? (module.input.private_alb_https_listener_arn || module.input.private_alb_http_listener_arn) : (get(get(module.input.private_albs_by_name, module.input.private_alb_name), "https_listener_arn") || get(get(module.input.private_albs_by_name, module.input.private_alb_name), "http_listener_arn"))) >>'
                 priority: << module.input.listener_rule_priority >>
             target_group:
               health_check:
@@
                 enabled: << module.input.target_group_stickiness_enabled >>
                 type: '<< module.input.target_group_stickiness_enabled ? module.input.target_group_stickiness_type : "lb_cookie" >>'
               target_type: ip
-          load_balancer_security_group_id: "<< module.input.public_web_service_enabled ? module.input.public_alb_security_group_id : module.input.private_alb_security_group_id >>"
+          load_balancer_security_group_id: '<< module.input.public_web_service_enabled ? ((module.input.public_alb_name || "") == "" ? module.input.public_alb_security_group_id : get(get(module.input.public_albs_by_name, module.input.public_alb_name), "security_group_id")) : ((module.input.private_alb_name || "") == "" ? module.input.private_alb_security_group_id : get(get(module.input.private_albs_by_name, module.input.private_alb_name), "security_group_id")) >>'
           name: << module.input.name >>
           network_mode: awsvpc
           new_deployment_forcing_enabled: << module.input.new_deployment_forcing_enabled >>
@@
       source:
         aws_account_id: << module.input.aws_account_id >>
         dimensions:
-          LoadBalancer: "<< module.input.public_web_service_enabled ? module.input.public_alb_arn_suffix : module.input.private_alb_arn_suffix >>"
+          LoadBalancer: '<< module.input.public_web_service_enabled ? ((module.input.public_alb_name || "") == "" ? module.input.public_alb_arn_suffix : get(get(module.input.public_albs_by_name, module.input.public_alb_name), "arn_suffix")) : ((module.input.private_alb_name || "") == "" ? module.input.private_alb_arn_suffix : get(get(module.input.private_albs_by_name, module.input.private_alb_name), "arn_suffix")) >>'
           TargetGroup: << stack.output.target_group_arn_suffix >>
         name: RequestCount
         namespace: AWS/ApplicationELB
@@
       source:
         aws_account_id: << module.input.aws_account_id >>
         dimensions:
-          LoadBalancer: "<< module.input.public_web_service_enabled ? module.input.public_alb_arn_suffix : module.input.private_alb_arn_suffix >>"
+          LoadBalancer: '<< module.input.public_web_service_enabled ? ((module.input.public_alb_name || "") == "" ? module.input.public_alb_arn_suffix : get(get(module.input.public_albs_by_name, module.input.public_alb_name), "arn_suffix")) : ((module.input.private_alb_name || "") == "" ? module.input.private_alb_arn_suffix : get(get(module.input.private_albs_by_name, module.input.private_alb_name), "arn_suffix")) >>'
           TargetGroup: << stack.output.target_group_arn_suffix >>
         name: HTTPCode_Target_5XX_Count
         namespace: AWS/ApplicationELB
@@
       source:
         aws_account_id: << module.input.aws_account_id >>
         dimensions:
-          LoadBalancer: "<< module.input.public_web_service_enabled ? module.input.public_alb_arn_suffix : module.input.private_alb_arn_suffix >>"
+          LoadBalancer: '<< module.input.public_web_service_enabled ? ((module.input.public_alb_name || "") == "" ? module.input.public_alb_arn_suffix : get(get(module.input.public_albs_by_name, module.input.public_alb_name), "arn_suffix")) : ((module.input.private_alb_name || "") == "" ? module.input.private_alb_arn_suffix : get(get(module.input.private_albs_by_name, module.input.private_alb_name), "arn_suffix")) >>'
           TargetGroup: << stack.output.target_group_arn_suffix >>
         name: HTTPCode_Target_4XX_Count
         namespace: AWS/ApplicationELB
@@
       source:
         aws_account_id: << module.input.aws_account_id >>
         dimensions:
-          LoadBalancer: "<< module.input.public_web_service_enabled ? module.input.public_alb_arn_suffix : module.input.private_alb_arn_suffix >>"
+          LoadBalancer: '<< module.input.public_web_servic
... diff truncated ...

Co-Authored-By: brandon <brandon@flightcontrol.dev>
Comment on lines +679 to +685
public_albs: >-
<< map(module.input.public_albs != nil ? module.input.public_albs : [], {"name": #.name,
"https_enabled": #.https_enabled, "certificate_arns": ((#.certificate_arns != nil ?
#.certificate_arns : []) | concat(#.additional_certificate_arns != nil ?
#.additional_certificate_arns : [])), "ssl_policy": #.ssl_policy, "idle_timeout":
#.idle_timeout, "web_acl_arn": #.web_acl_arn, "access_logs_enabled": #.access_logs_enabled,
"access_logs_bucket_arn": #.access_logs_bucket_arn}) >>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 security Public ALB ingress controls are dropped

When a cluster requires a public ALB restricted by CIDRs or source security groups, this mapping omits all three ingress fields, so Terraform applies 0.0.0.0/0, ::/0, and no source security groups, leaving the listener internet-accessible instead of enforcing the intended restrictions.

How this was verified: The public mapping omits the fields that the corresponding private mapping forwards, and the Terraform object defaults them to unrestricted IPv4 and IPv6 ingress.

Knowledge Base Used:

Prompt To Fix With AI
This is a comment left during a code review.
Path: compute/ecs_cluster/rvn-ecs-cluster-definition.yml
Line: 679-685

Comment:
**Public ALB ingress controls are dropped**

When a cluster requires a public ALB restricted by CIDRs or source security groups, this mapping omits all three ingress fields, so Terraform applies `0.0.0.0/0`, `::/0`, and no source security groups, leaving the listener internet-accessible instead of enforcing the intended restrictions.

**How this was verified:** The public mapping omits the fields that the corresponding private mapping forwards, and the Terraform object defaults them to unrestricted IPv4 and IPv6 ingress.

**Knowledge Base Used:**
- [ECS Cluster and ECS Service Modules](https://app.greptile.com/flightcontrol/-/custom-context/knowledge-base/flightcontrolhq/modules/-/docs/compute-ecs.md)
- [Terraform Stack module and shared partials](https://app.greptile.com/flightcontrol/-/custom-context/knowledge-base/flightcontrolhq/modules/-/docs/stack-and-partials.md)

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Comment on lines +344 to +351
(concat(filter(module.input.public_albs, #.name == module.input.public_alb_name),
module.input.public_albs)[0].https_listener_arn ||
concat(filter(module.input.public_albs, #.name == module.input.public_alb_name),
module.input.public_albs)[0].http_listener_arn) :
(concat(filter(module.input.private_albs, #.name == module.input.private_alb_name),
module.input.private_albs)[0].https_listener_arn ||
concat(filter(module.input.private_albs, #.name == module.input.private_alb_name),
module.input.private_albs)[0].http_listener_arn) >>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Stale selectors bind another load balancer

When a selected load balancer is renamed or removed, filter returns an empty list and concat(..., module.input.public_albs)[0] silently selects the first remaining entry, causing the service listener, security group, and metric dimensions to point at a different ALB. The same fallback pattern also affects the NLB definition.

Knowledge Base Used: ECS Cluster and ECS Service Modules

Prompt To Fix With AI
This is a comment left during a code review.
Path: compute/ecs_service/rvn-ecs-web-definition.yml
Line: 344-351

Comment:
**Stale selectors bind another load balancer**

When a selected load balancer is renamed or removed, `filter` returns an empty list and `concat(..., module.input.public_albs)[0]` silently selects the first remaining entry, causing the service listener, security group, and metric dimensions to point at a different ALB. The same fallback pattern also affects the NLB definition.

**Knowledge Base Used:** [ECS Cluster and ECS Service Modules](https://app.greptile.com/flightcontrol/-/custom-context/knowledge-base/flightcontrolhq/modules/-/docs/compute-ecs.md)

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

devin-ai-integration Bot and others added 2 commits August 7, 2026 20:42
…blic ALB ingress mapping

Co-Authored-By: brandon <brandon@flightcontrol.dev>
…nputs

Co-Authored-By: brandon <brandon@flightcontrol.dev>
@flybayer
flybayer requested a review from mabadir August 8, 2026 20:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant