Skip to content

EKS and ECS services: let deploy steps set allowlisted environment variables - #253

Merged
ravion-ops[bot] merged 8 commits into
mainfrom
devin/1793135144-deploy-env-allowlist
Oct 31, 2026
Merged

ravion-ops[bot] merged 8 commits into
mainfrom
devin/1793135144-deploy-env-allowlist

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor
  • In merge queue / Merged
  • Prioritize

Summary

What we're trying to do: remove the resolve_runner_release step from the Ravion CICD pipeline. Every run spends about 65s (29–150s) getting a sandbox ready for it, and the work itself takes about 1s. The step exists only to compute the runner release string that the Tower image bakes in at build time. The plan is to compute that string in the runner upload step instead and hand it to Tower when it's deployed, as RUNNER_VERSION. EKS and ECS service modules can't take an env var from a deploy step today, and this PR adds that, safely.

Before: the app container's env (EKS pod env, ECS task definition environment) came only from the module instance's environment_variables. A pipeline deploy step could pass image_ref and nothing else.

After: a deploy step can pass input.environment_variables, but only for names the module instance allowlists in the new deploy_environment_variable_names input. Anyone who can trigger a deploy must not get to inject arbitrary env vars (e.g. HTTPS_PROXY, LD_PRELOAD, auth flags), so a deploy value is dropped unless all of these hold:

  • its name is in deploy_environment_variable_names (empty or unset means nothing gets through);
  • it has a plain value (value_from / secret references are never accepted);
  • it doesn't share a name with any of the module's environment_variables or secrets, so it can never replace a module-defined value;
  • on rvn-eks-web, rvn-ecs-web and rvn-ecs-nlb, it isn't PORT.

Changing the allowlist means editing the module instance, which is the same permission that already controls its env vars.

# module instance (ravion.yaml)
deploy_environment_variable_names: [RUNNER_VERSION]
# pipeline deploy step
input:
  image_ref: <<steps.build_tower.output.build_ref>>
  environment_variables:
    - name: RUNNER_VERSION
      value: <<steps.run_runner_upload.output.runner_release>>
  • The filter lives in the shared partials/deploy/eks-workload-helm-values-common.yml (worker and cron), in the EKS web definition's PORT-prefixed env, and in the app container environment of both task-definition variants (with and without FireLens) in the ECS web, worker and NLB definitions. Sidecar, FireLens and pre/post-deploy override env are unchanged; ECS overrides layer on the task definition, so pre/post-deploy tasks still get allowed values.
  • Both families declare the optional environment_variables deploy input (partials/deploy/eks-workload-deploy-common.yml, partials/deploy/ecs-service-deploy-common.yml).
  • The expression starts with ( because the API's template validator only checks the leading identifier.
  • test/deploy_env_allowlist evaluates each of these expressions with expr-lang (the engine Ravion uses) and checks the exact env list for: unset/empty allowlist, an allowlisted name plus an unlisted HTTPS_PROXY, collisions with module env and secrets, value_from only, missing deploy input, PORT, and a non-string value. It runs in the Validate Module Definitions job.

Versions:

  • rvn-eks-worker 0.17.0 → 0.18.0
  • rvn-eks-web 1.16.0 → 1.17.0
  • rvn-ecs-web 2.8.0 → 2.9.0
  • rvn-ecs-worker 2.4.0 → 2.5.0
  • rvn-ecs-nlb 2.5.0 → 2.6.0
  • rvn-eks-cron is unpublished, so no bump.

Follow-ups: a flightcontrol PR bumps eks-tower and allowlists RUNNER_VERSION, then a second flightcontrol PR removes resolve_runner_release.

Link to Devin session: https://app.devin.ai/sessions/a65ede619c9d4171b242b95d57a991dd
Open in Devin Desktop: https://app.devin.ai/desktop/session/a65ede619c9d4171b242b95d57a991dd?variant=devin

RetriggerView in GreptileConfidence Score: 4/5

The PR appears safe to merge, with a non-blocking gap in tests for the deploy filter.

Findings

  1. P2 Tests skip the filter's results ▶
Fix with agent prompt
### Issue 1
tools/ravion-modules/test/compiler.test.ts:510-515
The new test compares the compiled expression with a copied string; it never checks the resulting environment variables. A mistake copied into both strings would pass, including one that allows a deploy value to replace a module secret.

Add tests through the real expression engine for missing or empty allowlists, allowed and unlisted names, collisions with module variables and secrets, `value_from`-only entries, absent deploy input, and web `PORT`. This would preserve the manual checks as repeatable tests.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

---

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

Summary

Adds deploy-provided environment variables to EKS web, worker, and cron workloads.

  • EKS workloads can receive allowlisted values from pipeline deploy steps.
Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[Deploy environment variables] --> B{Allowed name and plain value?}
  B -->|No| C[Drop entry]
  B -->|Yes| D{Module variable, secret, or web PORT?}
  D -->|Yes| C
  D -->|No| E[Keep name and string value]
  F[Module environment variables] --> G[Helm env values]
  E --> G
  G --> H[Workload pods]
Loading

Reviews (1) · Last reviewed commit: "Allow allowlisted deploy environment var..." · Reviewed by Greptile

Co-Authored-By: brandon <brandon@flightcontrol.dev>
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".

  • Disable automatic comment, CI, and merge conflict monitoring

@github-actions

github-actions Bot commented Oct 27, 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-ecs-nlb 2.5.0 2.6.0 Let pipeline deploy steps set allowlisted environment variables, such as a release version computed earlier in the pipeline.
rvn-ecs-web 2.8.0 2.9.0 Let pipeline deploy steps set allowlisted environment variables, such as a release version computed earlier in the pipeline.
rvn-ecs-worker 2.4.0 2.5.0 Let pipeline deploy steps set allowlisted environment variables, such as a release version computed earlier in the pipeline.
rvn-eks-web 1.16.0 1.17.0 Let pipeline deploy steps set allowlisted environment variables, such as a release version computed earlier in the pipeline.
rvn-eks-worker 0.17.0 0.18.0 Let pipeline deploy steps set allowlisted environment variables, such as a release version computed earlier in the pipeline.

Diffs

rvn-ecs-nlb 2.5.0 -> 2.6.0

--- remote
+++ compiled
       placeholder: sha256:... or latest or <run-id>-railpack
       required: true
       type: string
+    - description: Plain environment variables to set for this deploy. Only names listed in the module's Deploy-provided environment variables input are applied; others are ignored. Can't replace a runtime variable or secret.
+      id: environment_variables
+      label: Environment variables
+      placeholder: |-
+        - name: LOG_LEVEL
+          value: info
+      required: false
+      type: array
   post_deploy: '<< module.input.post_deploy_enabled && len(module.input.post_deploy_command) > 0 ? {"container_overrides": [{"name": stack.output.container_name, "command": module.input.post_deploy_command, "environment": module.input.post_deploy_environment_variables, "cpu": (module.input.post_deploy_cpu ? int(float(module.input.post_deploy_cpu) * 1024) : nil), "memory": (module.input.post_deploy_memory ? int(float(module.input.post_deploy_memory) * 1024) : nil)}], "cpu": string(module.input.post_deploy_cpu ? int(float(module.input.post_deploy_cpu) * 1024) : (module.input.capacity_provider == "ec2" ? int(float(module.input.task_cpu) * 1024) : int(float(module.input.fargate_size.vcpu) * 1024))), "memory": string(module.input.post_deploy_memory ? int(float(module.input.post_deploy_memory) * 1024) : (module.input.capacity_provider == "ec2" ? int(float(module.input.task_memory) * 1024) : int(float(module.input.fargate_size.memory_gb) * 1024))), "ephemeral_storage": (module.input.post_deploy_ephemeral_storage_size_gib ? {size_in_gib: module.input.post_deploy_ephemeral_storage_size_gib} : nil), "task_role_arn": stack.output.task_role_arn, "execution_role_arn": stack.output.execution_role_arn, "capacity_provider_strategy": (get(module.input.advanced_terraform_variables, "capacity_provider_strategies") != nil ? get(module.input.advanced_terraform_variables, "capacity_provider_strategies") : ((module.input.capacity_provider == "fargate" || module.input.additional_fargate_capacity_enabled ? [{capacity_provider: module.input.fargate_capacity_provider_name, weight: 1, base: 0}] : []) | concat(module.input.capacity_provider == "fargate_spot" || module.input.additional_fargate_spot_capacity_enabled ? [{capacity_provider: module.input.fargate_spot_capacity_provider_name, weight: 1, base: 0}] : []) | concat(module.input.capacity_provider == "ec2" || module.input.additional_ec2_capacity_enabled ? [{capacity_provider: module.input.ec2_capacity_provider_name, weight: 1, base: 0}] : []))), "network_configuration": {"awsvpc_configuration": {"subnets": (module.input.private_subnet_placement_enabled ? module.input.private_subnet_ids : module.input.public_subnet_ids), "security_groups": ([stack.output.security_group_id] | concat(module.input.security_group_ids != nil ? module.input.security_group_ids : [])), "assign_public_ip": (module.input.private_subnet_placement_enabled ? "DISABLED" : "ENABLED")}}, "enable_execute_command": module.input.execute_command_enabled, "timeout": module.input.post_deploy_timeout} : nil >>'
   pre_deploy: '<< module.input.pre_deploy_enabled && len(module.input.pre_deploy_command) > 0 ? {"container_overrides": [{"name": stack.output.container_name, "command": module.input.pre_deploy_command, "environment": module.input.pre_deploy_environment_variables, "cpu": (module.input.pre_deploy_cpu ? int(float(module.input.pre_deploy_cpu) * 1024) : nil), "memory": (module.input.pre_deploy_memory ? int(float(module.input.pre_deploy_memory) * 1024) : nil)}], "cpu": string(module.input.pre_deploy_cpu ? int(float(module.input.pre_deploy_cpu) * 1024) : (module.input.capacity_provider == "ec2" ? int(float(module.input.task_cpu) * 1024) : int(float(module.input.fargate_size.vcpu) * 1024))), "memory": string(module.input.pre_deploy_memory ? int(float(module.input.pre_deploy_memory) * 1024) : (module.input.capacity_provider == "ec2" ? int(float(module.input.task_memory) * 1024) : int(float(module.input.fargate_size.memory_gb) * 1024))), "ephemeral_storage": (module.input.pre_deploy_ephemeral_storage_size_gib ? {size_in_gib: module.input.pre_deploy_ephemeral_storage_size_gib} : nil), "task_role_arn": stack.output.task_role_arn, "execution_role_arn": stack.output.execution_role_arn, "capacity_provider_strategy": (get(module.input.advanced_terraform_variables, "capacity_provider_strategies") != nil ? get(module.input.advanced_terraform_variables, "capacity_provider_strategies") : ((module.input.capacity_provider == "fargate" || module.input.additional_fargate_capacity_enabled ? [{capacity_provider: module.input.fargate_capacity_provider_name, weight: 1, base: 0}] : []) | concat(module.input.capacity_provider == "fargate_spot" || module.input.additional_fargate_spot_capacity_enabled ? [{capacity_provider: module.input.fargate_spot_capacity_provider_name, weight: 1, base: 0}] : []) | concat(module.input.capacity_provider == "ec2" || module.input.additional_ec2_capacity_enabled ? [{capacity_provider: module.input.ec2_capacity_provider_name, weight: 1, base: 0}] : []))), "network_configuration": {"awsvpc_configuration": {"subnets": (module.input.private_subnet_placement_enabled ? module.input.private_subnet_ids : module.input.public_subnet_ids), "security_groups": ([stack.output.security_group_id] | concat(module.input.security_group_ids != nil ? module.input.security_group_ids : [])), "assign_public_ip": (module.input.private_subnet_placement_enabled ? "DISABLED" : "ENABLED")}}, "enable_execute_command": module.input.execute_command_enabled, "timeout": module.input.pre_deploy_timeout} : nil >>'
   strategy:
@@
     minimum_healthy_percent: "<< module.input.deployment_minimum_healthy_percent != nil ? module.input.deployment_minimum_healthy_percent : 100 >>"
     type: rolling
   task_definition:
-    container_definitions: "<< (module.input.firelens_enabled ? [{\"command\": (len(module.input.image_start_command) > 0 ? module.input.image_start_command : nil), \"cpu\": (module.input.capacity_provider == \"ec2\" ? int(float(module.input.task_cpu) * 1024) : int(float(module.input.fargate_size.vcpu) * 1024)), \"depends_on\": [{\"container_name\": \"log_router\", \"condition\": \"START\"}], \"environment\": ([{name: \"PORT\", value: string(module.input.listeners[0].container_port)}] | concat(module.input.environment_variables != nil ? module.input.environment_variables : [])), \"essential\": true, \"image\": (module.input.build_source == \"image_registry\" ? (deploy.input.image_ref contains \"sha256:\" ? module.input.image_repository + \"@\" + deploy.input.image_ref : module.input.image_repository + \":\" + deploy.input.image_ref) : (deploy.input.image_ref contains \"sha256:\" ? stack.output.ecr_repository_url + \"@\" + deploy.input.image_ref : stack.output.ecr_repository_url + \":\" + deploy.input.image_ref)), \"linux_parameters\": {\"init_process_enabled\": true}, \"log_configuration\": {\"log_driver\": \"awsfirelens\"}, \"memory\": (module.input.capacity_provider == \"ec2\" ? int(float(module.input.task_memory) * 1024) : int(float(module.input.fargate_size.memory_gb) * 1024)), \"mount_points\": (module.input.efs_enabled ? [{\"source_volume\": \"efs\", \"container_path\": module.input.efs_mount_path, \"read_only\": module.input.efs_read_only}] : []), \"name\": (stack.output.container_name), \"port_mappings\": map(module.input.listeners, {\"container_port\": #.container_port, \"protocol\": (#.listener_protocol == \"UDP\" ? \"udp\" : \"tcp\")}), \"readonly_root_filesystem\": false, \"repository_credentials\": (module.input.image_registry_credentials_secret_arn ? {credentials_parameter: module.input.image_registry_credentials_secret_arn} : nil), \"secrets\": (module.input.secrets), \"stop_timeout\": (module.input.stop_timeout != nil ? module.input.stop_timeout : 30)}, {\"name\": \"log_router\", \"image\": module.input.firelens_image, \"cpu\": 0, \"memory_reservation\": 51, \"essential\": true, \"environment\": ([{name: \"FIRELENS_CONFIG_CONTENT\", value: \"[SERVICE]\\n    Flush 1\\n    Grace 30\\n\\n\" + (module.input.firelens_config ? module.input.firelens_config + \"\\n\" : \"\") + (module.input.firelens_cloudwatch_output_enabled ? \"\\n[OUTPUT]\\n    Name cloudwatch\\n    Match *\\n    region \" + stack.output.region + \"\\n    log_group_name \" + stack.output.log_group_name + \"\\n    auto_create_group true\\n    log_stream_prefix \" + stack.output.log_stream_prefix + \"/\\n    retry_limit 2\\n    log_key log\\n    log_format json/emf\\n\" : \"\")}] | concat(module.input.firelens_environment_variables != nil ? module.input.firelens_environment_variables : [])), \"secrets\": module.input.firelens_secrets != nil ? module.input.firelens_secrets : [], \"command\": [\"/bin/sh\", \"-c\", \"printf '%s' \\\"$FIRELENS_CONFIG_CONTENT\\\" > /flightcontrol-firelens.conf && exec /entrypoint.sh\"], \"user\": \"0\", \"log_configuration\": {\"log_driver\": \"awslogs\", \"options\": {\"awslogs-group\": stack.output.log_group_name, \"awslogs-region\": stack.output.region, \"awslogs-stream-prefix\": stack.output.log_stream_prefix + \"/firelens\"}}, \"firelens_configuration\": {\"type\": \"fluentbit\", \"options\": {\"config-file-type\": \"file\", \"config-file-value\": \"/flightcontrol-firelens.conf\", \"enable-ecs-log-metadata\": string(module.input.firelens_ecs_metadata_enabled)}}}] : [{\"command\": (len(module.input.image_start_command) > 0 ? module.input.image_start_command : nil), \"cpu\": (module.input.capacity_provider == \"ec2\" ? int(float(module.input.task_cpu) * 1024) : int(float(module.input.fargate_size.vcpu) * 1024)), \"environment\": ([{name: \"PORT\", value: string(module.input.listeners[0].container_port)}] | concat(module.input.environment_variables != nil ? module.input.environment_variables : [])), \"essential\": true, \"image\": (module.input.build_source == \"image_registry\" ? (deploy.input.image_ref contains \"sha256:\" ? module.input.image_repository + \"@\" + deploy.input.image_ref : module.input.image_repository + \":\" + deploy.input.image_ref) : (deploy.input.image_ref contains \"sha256:\" ? stack.output.ecr_repository_url + \"@\" + deploy.input.image_ref : stack.output.ecr_repository_url + \":\" + deploy.input.image_ref)), \"linux_parameters\": {\"init_process_enabled\": true}, \"log_configuration\": {\"log_driver\": \"awslogs\", \"options\": {\"awslogs-group\": stack.output.log_group_name, \"awslogs-region\": stack.output.region, \"awslogs-stream-prefix\": stack.output.log_stream_prefix}}, \"memory\": (module.input.capacity_provider == \"ec2\" ? int(float(module.input.task_memory) * 1024) : int(float(module.input.fargate_size.memory_gb) * 1024)), \"mount_points\": (module.input.efs_enabled ? [{\"source_volume\": \"efs\", \"container_path\": module.input.efs_mount_path, \"read_only\": module.input.efs_read_only}] : []), \"name\": (stack.output.container_name), \"port_mappings\": map(module.input.listeners, {\"container_port\": #.container_port, \"protocol\": (#.listener_protocol == \"UDP\" ? \"udp\" : \"tcp\")}), \"readonly_root_filesystem\": false, \"repository_credentials\": (module.input.image_registry_credentials_secret_arn ? {credentials_parameter: module.input.image_registry_credentials_secret_arn} : nil), \"secrets\": (module.input.secrets), \"stop_timeout\": (module.input.stop_timeout != nil ? module.input.stop_timeout : 30)}]) | concat(module.input.sidecars ? map(module.input.sidecars, {\"name\": #.name, \"image\": #.image, \"essential\": #.essential, \"cpu\": (#.cpu ? int(float(#.cpu) * 1024) : nil), \"memory\": (#.memory ? int(float(#.memory) * 1024) : nil), \"memory_reservation\": (#.memory_reservation ? int(float(#.memory_reservation) * 1024) : nil), \"environment\": #.environment, \"secrets\": #.secrets, \"port_mappings\": #.port_mappings, \"mount_points\": #.mount_points, \"depends_on\": #.depends_on, \"command\": #.command, \"entry_point\": #.entry_point, \"working_directory\": #.working_directory}) : []) >>"
+    container_definitions: "
... diff truncated ...

rvn-ecs-web 2.8.0 -> 2.9.0

--- remote
+++ compiled
       placeholder: sha256:... or latest or <run-id>-railpack
       required: true
       type: string
+    - description: Plain environment variables to set for this deploy. Only names listed in the module's Deploy-provided environment variables input are applied; others are ignored. Can't replace a runtime variable or secret.
+      id: environment_variables
+      label: Environment variables
+      placeholder: |-
+        - name: LOG_LEVEL
+          value: info
+      required: false
+      type: array
   post_deploy: '<< module.input.post_deploy_enabled && len(module.input.post_deploy_command) > 0 ? {"container_overrides": [{"name": stack.output.container_name, "command": module.input.post_deploy_command, "environment": module.input.post_deploy_environment_variables, "cpu": (module.input.post_deploy_cpu ? int(float(module.input.post_deploy_cpu) * 1024) : nil), "memory": (module.input.post_deploy_memory ? int(float(module.input.post_deploy_memory) * 1024) : nil)}], "cpu": string(module.input.post_deploy_cpu ? int(float(module.input.post_deploy_cpu) * 1024) : (module.input.capacity_provider == "ec2" ? int(float(module.input.task_cpu) * 1024) : int(float(module.input.fargate_size.vcpu) * 1024))), "memory": string(module.input.post_deploy_memory ? int(float(module.input.post_deploy_memory) * 1024) : (module.input.capacity_provider == "ec2" ? int(float(module.input.task_memory) * 1024) : int(float(module.input.fargate_size.memory_gb) * 1024))), "ephemeral_storage": (module.input.post_deploy_ephemeral_storage_size_gib ? {size_in_gib: module.input.post_deploy_ephemeral_storage_size_gib} : nil), "task_role_arn": stack.output.task_role_arn, "execution_role_arn": stack.output.execution_role_arn, "capacity_provider_strategy": (get(module.input.advanced_terraform_variables, "capacity_provider_strategies") != nil ? get(module.input.advanced_terraform_variables, "capacity_provider_strategies") : ((module.input.capacity_provider == "fargate" || module.input.additional_fargate_capacity_enabled ? [{capacity_provider: module.input.fargate_capacity_provider_name, weight: 1, base: 0}] : []) | concat(module.input.capacity_provider == "fargate_spot" || module.input.additional_fargate_spot_capacity_enabled ? [{capacity_provider: module.input.fargate_spot_capacity_provider_name, weight: 1, base: 0}] : []) | concat(module.input.capacity_provider == "ec2" || module.input.additional_ec2_capacity_enabled ? [{capacity_provider: module.input.ec2_capacity_provider_name, weight: 1, base: 0}] : []))), "network_configuration": {"awsvpc_configuration": {"subnets": (module.input.private_subnet_placement_enabled ? module.input.private_subnet_ids : module.input.public_subnet_ids), "security_groups": ([stack.output.security_group_id] | concat(module.input.security_group_ids != nil ? module.input.security_group_ids : [])), "assign_public_ip": (module.input.private_subnet_placement_enabled ? "DISABLED" : "ENABLED")}}, "enable_execute_command": module.input.execute_command_enabled, "timeout": module.input.post_deploy_timeout} : nil >>'
   pre_deploy: '<< module.input.pre_deploy_enabled && len(module.input.pre_deploy_command) > 0 ? {"container_overrides": [{"name": stack.output.container_name, "command": module.input.pre_deploy_command, "environment": module.input.pre_deploy_environment_variables, "cpu": (module.input.pre_deploy_cpu ? int(float(module.input.pre_deploy_cpu) * 1024) : nil), "memory": (module.input.pre_deploy_memory ? int(float(module.input.pre_deploy_memory) * 1024) : nil)}], "cpu": string(module.input.pre_deploy_cpu ? int(float(module.input.pre_deploy_cpu) * 1024) : (module.input.capacity_provider == "ec2" ? int(float(module.input.task_cpu) * 1024) : int(float(module.input.fargate_size.vcpu) * 1024))), "memory": string(module.input.pre_deploy_memory ? int(float(module.input.pre_deploy_memory) * 1024) : (module.input.capacity_provider == "ec2" ? int(float(module.input.task_memory) * 1024) : int(float(module.input.fargate_size.memory_gb) * 1024))), "ephemeral_storage": (module.input.pre_deploy_ephemeral_storage_size_gib ? {size_in_gib: module.input.pre_deploy_ephemeral_storage_size_gib} : nil), "task_role_arn": stack.output.task_role_arn, "execution_role_arn": stack.output.execution_role_arn, "capacity_provider_strategy": (get(module.input.advanced_terraform_variables, "capacity_provider_strategies") != nil ? get(module.input.advanced_terraform_variables, "capacity_provider_strategies") : ((module.input.capacity_provider == "fargate" || module.input.additional_fargate_capacity_enabled ? [{capacity_provider: module.input.fargate_capacity_provider_name, weight: 1, base: 0}] : []) | concat(module.input.capacity_provider == "fargate_spot" || module.input.additional_fargate_spot_capacity_enabled ? [{capacity_provider: module.input.fargate_spot_capacity_provider_name, weight: 1, base: 0}] : []) | concat(module.input.capacity_provider == "ec2" || module.input.additional_ec2_capacity_enabled ? [{capacity_provider: module.input.ec2_capacity_provider_name, weight: 1, base: 0}] : []))), "network_configuration": {"awsvpc_configuration": {"subnets": (module.input.private_subnet_placement_enabled ? module.input.private_subnet_ids : module.input.public_subnet_ids), "security_groups": ([stack.output.security_group_id] | concat(module.input.security_group_ids != nil ? module.input.security_group_ids : [])), "assign_public_ip": (module.input.private_subnet_placement_enabled ? "DISABLED" : "ENABLED")}}, "enable_execute_command": module.input.execute_command_enabled, "timeout": module.input.pre_deploy_timeout} : nil >>'
   strategy: |
@@
     }
     >>
   task_definition:
-    container_definitions: "<< (module.input.firelens_enabled ? [{\"command\": (len(module.input.image_start_command) > 0 ? module.input.image_start_command : nil), \"cpu\": (module.input.capacity_provider == \"ec2\" ? int(float(module.input.task_cpu) * 1024) : int(float(module.input.fargate_size.vcpu) * 1024)), \"depends_on\": [{\"container_name\": \"log_router\", \"condition\": \"START\"}], \"environment\": ([{name: \"PORT\", value: string(module.input.container_port)}] | concat(module.input.environment_variables != nil ? module.input.environment_variables : [])), \"essential\": true, \"image\": (module.input.build_source == \"image_registry\" ? (deploy.input.image_ref contains \"sha256:\" ? module.input.image_repository + \"@\" + deploy.input.image_ref : module.input.image_repository + \":\" + deploy.input.image_ref) : (deploy.input.image_ref contains \"sha256:\" ? stack.output.ecr_repository_url + \"@\" + deploy.input.image_ref : stack.output.ecr_repository_url + \":\" + deploy.input.image_ref)), \"linux_parameters\": {\"init_process_enabled\": true}, \"log_configuration\": {\"log_driver\": \"awsfirelens\"}, \"memory\": (module.input.capacity_provider == \"ec2\" ? int(float(module.input.task_memory) * 1024) : int(float(module.input.fargate_size.memory_gb) * 1024)), \"mount_points\": (module.input.efs_enabled ? [{\"source_volume\": \"efs\", \"container_path\": module.input.efs_mount_path, \"read_only\": module.input.efs_read_only}] : []), \"name\": (stack.output.container_name), \"port_mappings\": [{\"app_protocol\": \"http\", \"container_port\": (module.input.container_port), \"protocol\": \"tcp\"}], \"readonly_root_filesystem\": false, \"repository_credentials\": (module.input.image_registry_credentials_secret_arn ? {credentials_parameter: module.input.image_registry_credentials_secret_arn} : nil), \"secrets\": (module.input.secrets), \"stop_timeout\": (module.input.stop_timeout != nil ? module.input.stop_timeout : 30)}, {\"name\": \"log_router\", \"image\": module.input.firelens_image, \"cpu\": 0, \"memory_reservation\": 51, \"essential\": true, \"environment\": ([{name: \"FIRELENS_CONFIG_CONTENT\", value: \"[SERVICE]\\n    Flush 1\\n    Grace 30\\n\\n\" + (module.input.firelens_config ? module.input.firelens_config + \"\\n\" : \"\") + (module.input.firelens_cloudwatch_output_enabled ? \"\\n[OUTPUT]\\n    Name cloudwatch\\n    Match *\\n    region \" + stack.output.region + \"\\n    log_group_name \" + stack.output.log_group_name + \"\\n    auto_create_group true\\n    log_stream_prefix \" + stack.output.log_stream_prefix + \"/\\n    retry_limit 2\\n    log_key log\\n    log_format json/emf\\n\" : \"\")}] | concat(module.input.firelens_environment_variables != nil ? module.input.firelens_environment_variables : [])), \"secrets\": module.input.firelens_secrets != nil ? module.input.firelens_secrets : [], \"command\": [\"/bin/sh\", \"-c\", \"printf '%s' \\\"$FIRELENS_CONFIG_CONTENT\\\" > /flightcontrol-firelens.conf && exec /entrypoint.sh\"], \"user\": \"0\", \"log_configuration\": {\"log_driver\": \"awslogs\", \"options\": {\"awslogs-group\": stack.output.log_group_name, \"awslogs-region\": stack.output.region, \"awslogs-stream-prefix\": stack.output.log_stream_prefix + \"/firelens\"}}, \"firelens_configuration\": {\"type\": \"fluentbit\", \"options\": {\"config-file-type\": \"file\", \"config-file-value\": \"/flightcontrol-firelens.conf\", \"enable-ecs-log-metadata\": string(module.input.firelens_ecs_metadata_enabled)}}}] : [{\"command\": (len(module.input.image_start_command) > 0 ? module.input.image_start_command : nil), \"cpu\": (module.input.capacity_provider == \"ec2\" ? int(float(module.input.task_cpu) * 1024) : int(float(module.input.fargate_size.vcpu) * 1024)), \"environment\": ([{name: \"PORT\", value: string(module.input.container_port)}] | concat(module.input.environment_variables != nil ? module.input.environment_variables : [])), \"essential\": true, \"image\": (module.input.build_source == \"image_registry\" ? (deploy.input.image_ref contains \"sha256:\" ? module.input.image_repository + \"@\" + deploy.input.image_ref : module.input.image_repository + \":\" + deploy.input.image_ref) : (deploy.input.image_ref contains \"sha256:\" ? stack.output.ecr_repository_url + \"@\" + deploy.input.image_ref : stack.output.ecr_repository_url + \":\" + deploy.input.image_ref)), \"linux_parameters\": {\"init_process_enabled\": true}, \"log_configuration\": {\"log_driver\": \"awslogs\", \"options\": {\"awslogs-group\": stack.output.log_group_name, \"awslogs-region\": stack.output.region, \"awslogs-stream-prefix\": stack.output.log_stream_prefix}}, \"memory\": (module.input.capacity_provider == \"ec2\" ? int(float(module.input.task_memory) * 1024) : int(float(module.input.fargate_size.memory_gb) * 1024)), \"mount_points\": (module.input.efs_enabled ? [{\"source_volume\": \"efs\", \"container_path\": module.input.efs_mount_path, \"read_only\": module.input.efs_read_only}] : []), \"name\": (stack.output.container_name), \"port_mappings\": [{\"app_protocol\": \"http\", \"container_port\": (module.input.container_port), \"protocol\": \"tcp\"}], \"readonly_root_filesystem\": false, \"repository_credentials\": (module.input.image_registry_credentials_secret_arn ? {credentials_parameter: module.input.image_registry_credentials_secret_arn} : nil), \"secrets\": (module.input.secrets), \"stop_timeout\": (module.input.stop_timeout != nil ? module.input.stop_timeout : 30)}]) | concat(module.input.sidecars ? map(module.input.sidecars, {\"name\": #.name, \"image\": #.image, \"essential\": #.essential, \"cpu\": (#.cpu ? int(float(#.cpu) * 1024) : nil), \"memory\": (#.memory ? int(float(#.memory) * 1024) : nil), \"memory_reservation\": (#.memory_reservation ? int(float(#.memory_reservation) * 1024) : nil), \"environment\": #.environment, \"secrets\": #.secrets, \"port_mappings\": #.port_mappings, \"mount_points\": #.mount_points, \"depends_on\": #.depends_on, \"command\": #.command, \"entry_point\": #.entry_point, \"working_directory\": #.working_directory}) : []) >>"
+    container_definitions: "<< (module.input.firelens_enabled ? [{\"command\": (len(module.input.image_start_command) > 0 ? module.input.image_start_command : nil), \"cpu\": (module.input.capacity_provider == \"ec2\" ? int(float(module.input.task_cpu) * 1024) : int(f
... diff truncated ...

rvn-ecs-worker 2.4.0 -> 2.5.0

--- remote
+++ compiled
       placeholder: sha256:... or latest or <run-id>-railpack
       required: true
       type: string
+    - description: Plain environment variables to set for this deploy. Only names listed in the module's Deploy-provided environment variables input are applied; others are ignored. Can't replace a runtime variable or secret.
+      id: environment_variables
+      label: Environment variables
+      placeholder: |-
+        - name: LOG_LEVEL
+          value: info
+      required: false
+      type: array
   post_deploy: '<< module.input.post_deploy_enabled && len(module.input.post_deploy_command) > 0 ? {"container_overrides": [{"name": stack.output.container_name, "command": module.input.post_deploy_command, "environment": module.input.post_deploy_environment_variables, "cpu": (module.input.post_deploy_cpu ? int(float(module.input.post_deploy_cpu) * 1024) : nil), "memory": (module.input.post_deploy_memory ? int(float(module.input.post_deploy_memory) * 1024) : nil)}], "cpu": string(module.input.post_deploy_cpu ? int(float(module.input.post_deploy_cpu) * 1024) : (module.input.capacity_provider == "ec2" ? int(float(module.input.task_cpu) * 1024) : int(float(module.input.fargate_size.vcpu) * 1024))), "memory": string(module.input.post_deploy_memory ? int(float(module.input.post_deploy_memory) * 1024) : (module.input.capacity_provider == "ec2" ? int(float(module.input.task_memory) * 1024) : int(float(module.input.fargate_size.memory_gb) * 1024))), "ephemeral_storage": (module.input.post_deploy_ephemeral_storage_size_gib ? {size_in_gib: module.input.post_deploy_ephemeral_storage_size_gib} : nil), "task_role_arn": stack.output.task_role_arn, "execution_role_arn": stack.output.execution_role_arn, "capacity_provider_strategy": (get(module.input.advanced_terraform_variables, "capacity_provider_strategies") != nil ? get(module.input.advanced_terraform_variables, "capacity_provider_strategies") : ((module.input.capacity_provider == "fargate" || module.input.additional_fargate_capacity_enabled ? [{capacity_provider: module.input.fargate_capacity_provider_name, weight: 1, base: 0}] : []) | concat(module.input.capacity_provider == "fargate_spot" || module.input.additional_fargate_spot_capacity_enabled ? [{capacity_provider: module.input.fargate_spot_capacity_provider_name, weight: 1, base: 0}] : []) | concat(module.input.capacity_provider == "ec2" || module.input.additional_ec2_capacity_enabled ? [{capacity_provider: module.input.ec2_capacity_provider_name, weight: 1, base: 0}] : []))), "network_configuration": {"awsvpc_configuration": {"subnets": (module.input.private_subnet_placement_enabled ? module.input.private_subnet_ids : module.input.public_subnet_ids), "security_groups": ([stack.output.security_group_id] | concat(module.input.security_group_ids != nil ? module.input.security_group_ids : [])), "assign_public_ip": (module.input.private_subnet_placement_enabled ? "DISABLED" : "ENABLED")}}, "enable_execute_command": module.input.execute_command_enabled, "timeout": module.input.post_deploy_timeout} : nil >>'
   pre_deploy: '<< module.input.pre_deploy_enabled && len(module.input.pre_deploy_command) > 0 ? {"container_overrides": [{"name": stack.output.container_name, "command": module.input.pre_deploy_command, "environment": module.input.pre_deploy_environment_variables, "cpu": (module.input.pre_deploy_cpu ? int(float(module.input.pre_deploy_cpu) * 1024) : nil), "memory": (module.input.pre_deploy_memory ? int(float(module.input.pre_deploy_memory) * 1024) : nil)}], "cpu": string(module.input.pre_deploy_cpu ? int(float(module.input.pre_deploy_cpu) * 1024) : (module.input.capacity_provider == "ec2" ? int(float(module.input.task_cpu) * 1024) : int(float(module.input.fargate_size.vcpu) * 1024))), "memory": string(module.input.pre_deploy_memory ? int(float(module.input.pre_deploy_memory) * 1024) : (module.input.capacity_provider == "ec2" ? int(float(module.input.task_memory) * 1024) : int(float(module.input.fargate_size.memory_gb) * 1024))), "ephemeral_storage": (module.input.pre_deploy_ephemeral_storage_size_gib ? {size_in_gib: module.input.pre_deploy_ephemeral_storage_size_gib} : nil), "task_role_arn": stack.output.task_role_arn, "execution_role_arn": stack.output.execution_role_arn, "capacity_provider_strategy": (get(module.input.advanced_terraform_variables, "capacity_provider_strategies") != nil ? get(module.input.advanced_terraform_variables, "capacity_provider_strategies") : ((module.input.capacity_provider == "fargate" || module.input.additional_fargate_capacity_enabled ? [{capacity_provider: module.input.fargate_capacity_provider_name, weight: 1, base: 0}] : []) | concat(module.input.capacity_provider == "fargate_spot" || module.input.additional_fargate_spot_capacity_enabled ? [{capacity_provider: module.input.fargate_spot_capacity_provider_name, weight: 1, base: 0}] : []) | concat(module.input.capacity_provider == "ec2" || module.input.additional_ec2_capacity_enabled ? [{capacity_provider: module.input.ec2_capacity_provider_name, weight: 1, base: 0}] : []))), "network_configuration": {"awsvpc_configuration": {"subnets": (module.input.private_subnet_placement_enabled ? module.input.private_subnet_ids : module.input.public_subnet_ids), "security_groups": ([stack.output.security_group_id] | concat(module.input.security_group_ids != nil ? module.input.security_group_ids : [])), "assign_public_ip": (module.input.private_subnet_placement_enabled ? "DISABLED" : "ENABLED")}}, "enable_execute_command": module.input.execute_command_enabled, "timeout": module.input.pre_deploy_timeout} : nil >>'
   strategy:
@@
     minimum_healthy_percent: "<< module.input.deployment_minimum_healthy_percent != nil ? module.input.deployment_minimum_healthy_percent : 100 >>"
     type: rolling
   task_definition:
-    container_definitions: "<< (module.input.firelens_enabled ? [{\"command\": (len(module.input.image_start_command) > 0 ? module.input.image_start_command : nil), \"cpu\": (module.input.capacity_provider == \"ec2\" ? int(float(module.input.task_cpu) * 1024) : int(float(module.input.fargate_size.vcpu) * 1024)), \"depends_on\": [{\"container_name\": \"log_router\", \"condition\": \"START\"}], \"environment\": (module.input.environment_variables != nil ? module.input.environment_variables : []), \"essential\": true, \"image\": (module.input.build_source == \"image_registry\" ? (deploy.input.image_ref contains \"sha256:\" ? module.input.image_repository + \"@\" + deploy.input.image_ref : module.input.image_repository + \":\" + deploy.input.image_ref) : (deploy.input.image_ref contains \"sha256:\" ? stack.output.ecr_repository_url + \"@\" + deploy.input.image_ref : stack.output.ecr_repository_url + \":\" + deploy.input.image_ref)), \"linux_parameters\": {\"init_process_enabled\": true}, \"log_configuration\": {\"log_driver\": \"awsfirelens\"}, \"memory\": (module.input.capacity_provider == \"ec2\" ? int(float(module.input.task_memory) * 1024) : int(float(module.input.fargate_size.memory_gb) * 1024)), \"mount_points\": (module.input.efs_enabled ? [{\"source_volume\": \"efs\", \"container_path\": module.input.efs_mount_path, \"read_only\": module.input.efs_read_only}] : []), \"name\": (stack.output.container_name), \"readonly_root_filesystem\": false, \"repository_credentials\": (module.input.image_registry_credentials_secret_arn ? {credentials_parameter: module.input.image_registry_credentials_secret_arn} : nil), \"secrets\": (module.input.secrets), \"stop_timeout\": (module.input.stop_timeout != nil ? module.input.stop_timeout : 30)}, {\"name\": \"log_router\", \"image\": module.input.firelens_image, \"cpu\": 0, \"memory_reservation\": 51, \"essential\": true, \"environment\": ([{name: \"FIRELENS_CONFIG_CONTENT\", value: \"[SERVICE]\\n    Flush 1\\n    Grace 30\\n\\n\" + (module.input.firelens_config ? module.input.firelens_config + \"\\n\" : \"\") + (module.input.firelens_cloudwatch_output_enabled ? \"\\n[OUTPUT]\\n    Name cloudwatch\\n    Match *\\n    region \" + stack.output.region + \"\\n    log_group_name \" + stack.output.log_group_name + \"\\n    auto_create_group true\\n    log_stream_prefix \" + stack.output.log_stream_prefix + \"/\\n    retry_limit 2\\n    log_key log\\n    log_format json/emf\\n\" : \"\")}] | concat(module.input.firelens_environment_variables != nil ? module.input.firelens_environment_variables : [])), \"secrets\": module.input.firelens_secrets != nil ? module.input.firelens_secrets : [], \"command\": [\"/bin/sh\", \"-c\", \"printf '%s' \\\"$FIRELENS_CONFIG_CONTENT\\\" > /flightcontrol-firelens.conf && exec /entrypoint.sh\"], \"user\": \"0\", \"log_configuration\": {\"log_driver\": \"awslogs\", \"options\": {\"awslogs-group\": stack.output.log_group_name, \"awslogs-region\": stack.output.region, \"awslogs-stream-prefix\": stack.output.log_stream_prefix + \"/firelens\"}}, \"firelens_configuration\": {\"type\": \"fluentbit\", \"options\": {\"config-file-type\": \"file\", \"config-file-value\": \"/flightcontrol-firelens.conf\", \"enable-ecs-log-metadata\": string(module.input.firelens_ecs_metadata_enabled)}}}] : [{\"command\": (len(module.input.image_start_command) > 0 ? module.input.image_start_command : nil), \"cpu\": (module.input.capacity_provider == \"ec2\" ? int(float(module.input.task_cpu) * 1024) : int(float(module.input.fargate_size.vcpu) * 1024)), \"environment\": (module.input.environment_variables != nil ? module.input.environment_variables : []), \"essential\": true, \"image\": (module.input.build_source == \"image_registry\" ? (deploy.input.image_ref contains \"sha256:\" ? module.input.image_repository + \"@\" + deploy.input.image_ref : module.input.image_repository + \":\" + deploy.input.image_ref) : (deploy.input.image_ref contains \"sha256:\" ? stack.output.ecr_repository_url + \"@\" + deploy.input.image_ref : stack.output.ecr_repository_url + \":\" + deploy.input.image_ref)), \"linux_parameters\": {\"init_process_enabled\": true}, \"log_configuration\": {\"log_driver\": \"awslogs\", \"options\": {\"awslogs-group\": stack.output.log_group_name, \"awslogs-region\": stack.output.region, \"awslogs-stream-prefix\": stack.output.log_stream_prefix}}, \"memory\": (module.input.capacity_provider == \"ec2\" ? int(float(module.input.task_memory) * 1024) : int(float(module.input.fargate_size.memory_gb) * 1024)), \"mount_points\": (module.input.efs_enabled ? [{\"source_volume\": \"efs\", \"container_path\": module.input.efs_mount_path, \"read_only\": module.input.efs_read_only}] : []), \"name\": (stack.output.container_name), \"readonly_root_filesystem\": false, \"repository_credentials\": (module.input.image_registry_credentials_secret_arn ? {credentials_parameter: module.input.image_registry_credentials_secret_arn} : nil), \"secrets\": (module.input.secrets), \"stop_timeout\": (module.input.stop_timeout != nil ? module.input.stop_timeout : 30)}]) | concat(module.input.sidecars ? map(module.input.sidecars, {\"name\": #.name, \"image\": #.image, \"essential\": #.essential, \"cpu\": (#.cpu ? int(float(#.cpu) * 1024) : nil), \"memory\": (#.memory ? int(float(#.memory) * 1024) : nil), \"memory_reservation\": (#.memory_reservation ? int(float(#.memory_reservation) * 1024) : nil), \"environment\": #.environment, \"secrets\": #.secrets, \"port_mappings\": #.port_mappings, \"mount_points\": #.mount_points, \"depends_on\": #.depends_on, \"command\": #.command, \"entry_point\": #.entry_point, \"working_directory\": #.working_directory}) : []) >>"
+    container_definitions: "<< (module.input.firelens_enabled ? [{\"command\": (len(module.input.image_start_command) > 0 ? module.input.image_start_command : nil), \"cpu\": (module.input.capacity_provider == \"ec2\" ? int(float(module.input.task_cpu) * 1024) : int(float(module.input.fargate_size.vcpu) * 1024)), \"depends_on\": [{\"container_name\": \"log_router\", \"condition\": \"START\"}], \"environment\": ((module.input.environment_variables != nil ? module.input.environment_variables : []) | concat(mod
... diff truncated ...

rvn-eks-web 1.16.0 -> 1.17.0

--- remote
+++ compiled
     source:
       base_path: charts/rvn-eks-web
       branch: main
-      ref: rvn-eks-web@1.16.0
+      ref: rvn-eks-web@1.17.0
       repo_url: https://github.com/ravionhq/modules
       type: git
     type: helm
@@
         targetMemoryUtilizationPercentage: << module.input.autoscaling_memory_target >>
       command: "<< module.input.start_command != nil ? module.input.start_command : [] >>"
       containerPort: << module.input.container_port >>
-      env: '<< [{"name": "PORT", "value": string(module.input.container_port)}] | concat(module.input.environment_variables != nil ? module.input.environment_variables : []) >>'
+      env: '<< [{"name": "PORT", "value": string(module.input.container_port)}] | concat(module.input.environment_variables != nil ? module.input.environment_variables : []) | concat(module.input.deploy_environment_variable_names != nil && deploy.input.environment_variables != nil ? map(filter(deploy.input.environment_variables, #.name != "PORT" && #.name in module.input.deploy_environment_variable_names && #.value != nil && !(#.name in map(module.input.environment_variables ?? [], .name)) && !(#.name in map(module.input.secrets ?? [], .name))), {"name": #.name, "value": string(#.value)}) : []) >>'
       fullnameOverride: << module.input.name >>
       image:
         digest: '<< deploy.input.image_ref contains "sha256:" ? deploy.input.image_ref : "" >>'
@@
       placeholder: sha256:... or latest
       required: true
       type: string
+    - description: Plain environment variables to set for this deploy. Only names listed in the module's Deploy-provided environment variables input are applied; others are ignored. Can't replace a runtime variable or secret.
+      id: environment_variables
+      label: Environment variables
+      placeholder: |-
+        - name: LOG_LEVEL
+          value: info
+      required: false
+      type: array
   timeout: 1800
   type: aws:eks
 inputs:
@@
       - name: DATABASE_URL
         value_from: arn:aws:secretsmanager:us-east-1:123456789012:secret:db-AbCdEf:url::
     type: array
+  - add_button_label: Add env var name
+    collapsible: true
+    description: Names a pipeline deploy step may set with `input.environment_variables`. Plain values only; can't replace a runtime variable or secret.
+    id: deploy_environment_variable_names
+    label: Deploy-provided environment variables
+    required: false
+    type: string_array
   - id: section_iam
     label: IAM roles and policies
     type: section
@@
 
   Terraform owns the `aws_lb_target_group` and `aws_lb_listener_rule` against a shared load balancer, and the AWS Load Balancer Controller registers pod IPs into that target group. The chart does not create an Ingress, so no per-service load balancer is provisioned. This is the same routing shape as ECS Web Server, so a service moving from ECS keeps its routing model.
 
-  Chart source: [ravionhq/modules/charts/rvn-eks-web](https://github.com/ravionhq/modules/tree/rvn-eks-web@1.16.0/charts/rvn-eks-web)
+  Chart source: [ravionhq/modules/charts/rvn-eks-web](https://github.com/ravionhq/modules/tree/rvn-eks-web@1.17.0/charts/rvn-eks-web)
 
-  Terraform source: [ravionhq/modules/compute/eks_service](https://github.com/ravionhq/modules/tree/rvn-eks-web@1.16.0/compute/eks_service)
+  Terraform source: [ravionhq/modules/compute/eks_service](https://github.com/ravionhq/modules/tree/rvn-eks-web@1.17.0/compute/eks_service)
 
   ## Use cases
 
@@
 
   Use **Runtime environment variables** for plain values, as an array of `{name, value}` objects.
 
+  Use **Deploy-provided environment variables** to allow a pipeline deploy step to set selected plain values. They cannot replace a Runtime environment variable or Runtime secret, and `PORT` is reserved for the web service.
+
   Use **Runtime secrets** for sensitive values, as an array of `{name, value_from}` objects - the same shape as ECS, so a service migrating from ECS Web Server carries its secret configuration unchanged. `value_from` must be a full Secrets Manager or SSM Parameter Store ARN, including the JSON-key extraction suffix when you want a single key out of a JSON secret.
 
   Ravion never reads these values. The ARNs travel to the chart as references, the chart renders an `ExternalSecret`, and the External Secrets Operator materializes the Kubernetes Secret in the cluster. No secret value reaches Ravion, the Helm values document, or the Helm release history.
@@
   | Minimum available pods | No | 1 | Pods kept running during a node drain when the disruption budget is on |
   | Runtime environment variables | No | - | Array of {name, value} objects |
   | Runtime secrets | No | - | Array of {name, value_from} ARN references |
+  | Deploy-provided environment variables | No | - | Names a pipeline deploy step may set to plain values |
   | Create IAM role | No | false | Create an IAM role for the workload and bind it to its pods through EKS Pod Identity |
   | IAM role name | No | {service}-task | Explicit role name, for a service whose ECS task role already holds the default |
   | IAM policy ARNs | No | [] | Managed policy ARNs attached to the workload role |
@@
         execution_environment_overrides:
           security_group: << module.input.ravion_runner_security_group_id >>
         plan_permissions: '<< module.input.terraform_plan_default_policies_enabled == false ? {"replace": module.input.terraform_plan_iam_policy_arns} : (module.input.terraform_plan_iam_policy_arns ? {"attach": module.input.terraform_plan_iam_policy_arns} : nil) >>'
-        ref: rvn-eks-web@1.16.0
+        ref: rvn-eks-web@1.17.0
         repo: https://github.com/ravionhq/modules
         stack_id: <<stack.id>>
         terraform_variables:

rvn-eks-worker 0.17.0 -> 0.18.0

--- remote
+++ compiled
     source:
       base_path: charts/rvn-eks-worker
       branch: main
-      ref: rvn-eks-worker@0.17.0
+      ref: rvn-eks-worker@0.18.0
       repo_url: https://github.com/ravionhq/modules
       type: git
     type: helm
@@
         targetCPUUtilizationPercentage: "<< module.input.autoscaling_cpu_target != nil ? module.input.autoscaling_cpu_target : 70 >>"
         targetMemoryUtilizationPercentage: << module.input.autoscaling_memory_target >>
       command: "<< module.input.start_command != nil ? module.input.start_command : [] >>"
-      env: "<< module.input.environment_variables != nil ? module.input.environment_variables : [] >>"
+      env: '<< (module.input.environment_variables != nil ? module.input.environment_variables : []) | concat(module.input.deploy_environment_variable_names != nil && deploy.input.environment_variables != nil ? map(filter(deploy.input.environment_variables, #.name in module.input.deploy_environment_variable_names && #.value != nil && !(#.name in map(module.input.environment_variables ?? [], .name)) && !(#.name in map(module.input.secrets ?? [], .name))), {"name": #.name, "value": string(#.value)}) : []) >>'
       fullnameOverride: << module.input.name >>
       image:
         digest: '<< deploy.input.image_ref contains "sha256:" ? deploy.input.image_ref : "" >>'
@@
       placeholder: sha256:... or latest
       required: true
       type: string
+    - description: Plain environment variables to set for this deploy. Only names listed in the module's Deploy-provided environment variables input are applied; others are ignored. Can't replace a runtime variable or secret.
+      id: environment_variables
+      label: Environment variables
+      placeholder: |-
+        - name: LOG_LEVEL
+          value: info
+      required: false
+      type: array
   timeout: 1800
   type: aws:eks
 inputs:
@@
       - name: DATABASE_URL
         value_from: arn:aws:secretsmanager:us-east-1:123456789012:secret:db-AbCdEf:url::
     type: array
+  - add_button_label: Add env var name
+    collapsible: true
+    description: Names a pipeline deploy step may set with `input.environment_variables`. Plain values only; can't replace a runtime variable or secret.
+    id: deploy_environment_variable_names
+    label: Deploy-provided environment variables
+    required: false
+    type: string_array
   - id: section_iam
     label: IAM roles and policies
     type: section
@@
 
   The Terraform stack creates the ECR repository that Dockerfile and Railpack builds push to and, when Fargate is selected, a workload-specific EKS Fargate profile and pod execution role. On the Pull from image registry path with automatic or EC2 compute, the stack creates no AWS resources. Everything else the module needs - cluster endpoint, certificate authority data, private subnet IDs, and the Ravion Runner role - comes from the selected EKS Cluster module.
 
-  Chart source: [ravionhq/modules/charts/rvn-eks-worker](https://github.com/ravionhq/modules/tree/rvn-eks-worker@0.17.0/charts/rvn-eks-worker)
+  Chart source: [ravionhq/modules/charts/rvn-eks-worker](https://github.com/ravionhq/modules/tree/rvn-eks-worker@0.18.0/charts/rvn-eks-worker)
 
-  Terraform source: [ravionhq/modules/compute/eks_service](https://github.com/ravionhq/modules/tree/rvn-eks-worker@0.17.0/compute/eks_service)
+  Terraform source: [ravionhq/modules/compute/eks_service](https://github.com/ravionhq/modules/tree/rvn-eks-worker@0.18.0/compute/eks_service)
 
   ## Use cases
 
@@
 
   Use **Runtime environment variables** for plain values, as an array of `{name, value}` objects.
 
+  Use **Deploy-provided environment variables** to allow a pipeline deploy step to set selected plain values. They cannot replace a Runtime environment variable or Runtime secret.
+
   Use **Runtime secrets** for sensitive values, as an array of `{name, value_from}` objects - the same shape as ECS. `value_from` must be a full Secrets Manager or SSM Parameter Store ARN, including the JSON-key extraction suffix when you want a single key out of a JSON secret.
 
   Ravion never reads these values. The ARNs travel to the chart as references, the chart renders an `ExternalSecret`, and the External Secrets Operator materializes the Kubernetes Secret in the cluster. No secret value reaches Ravion, the Helm values document, or the Helm release history.
@@
   | Stop timeout (secs) | No | 30 | Drain window after SIGTERM |
   | Runtime environment variables | No | - | Array of {name, value} objects |
   | Runtime secrets | No | - | Array of {name, value_from} ARN references |
+  | Deploy-provided environment variables | No | - | Names a pipeline deploy step may set to plain values |
   | Create IAM role | No | false | Create an IAM role for the workload and bind it to its pods through EKS Pod Identity |
   | IAM role name | No | {service}-task | Explicit role name, for a service whose ECS task role already holds the default |
   | IAM policy ARNs | No | [] | Managed policy ARNs attached to the workload role |
@@
         execution_environment_overrides:
           security_group: << module.input.ravion_runner_security_group_id >>
         plan_permissions: '<< module.input.terraform_plan_default_policies_enabled == false ? {"replace": module.input.terraform_plan_iam_policy_arns} : (module.input.terraform_plan_iam_policy_arns ? {"attach": module.input.terraform_plan_iam_policy_arns} : nil) >>'
-        ref: rvn-eks-worker@0.17.0
+        ref: rvn-eks-worker@0.18.0
         repo: https://github.com/ravionhq/modules
         stack_id: <<stack.id>>
         terraform_variables:

Co-Authored-By: brandon <brandon@flightcontrol.dev>
@github-actions
github-actions Bot requested a review from flybayer October 27, 2026 21:29
Co-Authored-By: brandon <brandon@flightcontrol.dev>

Copy link
Copy Markdown
Member

@devin-ai-integration do this for ECS mods too

Co-Authored-By: brandon <brandon@flightcontrol.dev>
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

@flybayer Done in 90f8048. rvn-ecs-web (2.7.0), rvn-ecs-worker (2.4.0) and rvn-ecs-nlb (2.5.0) now have the same deploy_environment_variable_names allowlist and the optional environment_variables deploy input.

The rules match EKS:

  • plain values only
  • a deploy value never replaces a module env var or secret
  • PORT is reserved on web and NLB

The filter covers the app container in both task-definition variants, with and without FireLens. Pre/post-deploy tasks pick up allowed values from the task definition. The runtime test checks all six ECS expressions.

@devin-ai-integration devin-ai-integration Bot changed the title EKS worker/web: let deploy steps set allowlisted environment variables EKS and ECS services: let deploy steps set allowlisted environment variables Oct 27, 2026
@flybayer
flybayer requested review from flybayer and mabadir October 28, 2026 15:09
devin-ai-integration Bot and others added 3 commits October 30, 2026 19:29
Co-Authored-By: brandon <brandon@flightcontrol.dev>
Co-Authored-By: brandon <brandon@flightcontrol.dev>
Co-Authored-By: brandon <brandon@flightcontrol.dev>
@ravion-ops
ravion-ops Bot merged commit ff2e48e into main Oct 31, 2026
28 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants