@@ -27,7 +27,7 @@ locals {
2727 dapr_app_id = local. has_dapr_sidecar && try (tostring (local. dapr_sidecar . appId ), " " ) != " " ? tostring (local. dapr_sidecar . appId ) : local. normalized_name
2828 dapr_app_port = local. has_dapr_sidecar && try (local. dapr_sidecar . appPort , null ) != null ? tostring (local. dapr_sidecar . appPort ) : null
2929 dapr_config_name = local. has_dapr_sidecar && try (tostring (local. dapr_sidecar . config ), " " ) != " " ? tostring (local. dapr_sidecar . config ) : null
30- dapr_annotations = local. has_dapr_sidecar ? merge (
30+ dapr_annotations = local. has_dapr_sidecar ? merge (
3131 {
3232 " dapr.io/enabled" = " true"
3333 " dapr.io/app-id" = local.dapr_app_id
@@ -36,20 +36,52 @@ locals {
3636 local. dapr_config_name != null ? { " dapr.io/config" = local.dapr_config_name } : {}
3737 ) : {}
3838
39- # Connections - used for linked resources like persistent volumes
39+ # Connections - merged resource data from linked resources
4040 connections = try (var. context . resource . connections , {})
4141
42- # Connection-derived environment variables, enabled when disableDefaultEnvVars is false
42+ # Connection definitions - original connection config (has source, disableDefaultEnvVars)
43+ connection_definitions = try (var. context . resource . properties . connections , {})
44+
45+ # Properties to exclude from connection environment variables
46+ excluded_properties = [" recipe" , " status" , " provisioningState" ]
47+
48+ # Helper to check if a connection is a secrets resource (from source in connection definition)
49+ is_secrets_resource = {
50+ for conn_name , conn_def in local . connection_definitions :
51+ conn_name = > can (regex (" Radius.Security/secrets" , try (conn_def. source , " " )))
52+ }
53+
54+ # Secrets connections to inject via envFrom.secretRef
55+ # The K8s secret name is the Radius resource name (last segment of the source ID)
56+ secrets_env_from = [
57+ for conn_name , conn in local . connections :
58+ {
59+ # Extract the secret name from the connection source (last segment of the resource ID)
60+ name = element (split (" /" , local. connection_definitions [conn_name ]. source ), length (split (" /" , local. connection_definitions [conn_name ]. source )) - 1 )
61+ prefix = upper (" CONNECTION_${ conn_name } _" )
62+ }
63+ if try (local. is_secrets_resource [conn_name ], false ) &&
64+ try (local. connection_definitions [conn_name ]. disableDefaultEnvVars , false ) != true
65+ ]
66+
67+ # Connection-derived environment variables for non-secrets connections
68+ # Secrets connections use envFrom.secretRef instead for cleaner injection
69+ # Each connection's resource properties become CONNECTION_<CONNECTION_NAME>_<PROPERTY_NAME>
70+ # Note: disableDefaultEnvVars is on connection_definitions, not the merged connections data
4371 connection_env_vars = flatten ([
4472 for conn_name , conn in local . connections :
45- try (conn. disableDefaultEnvVars , false )
46- ? [
47- for prop_name , prop_value in try (conn. status . computedValues , {}) : {
48- name = upper (" CONNECTION_${ conn_name } _${ prop_name } " )
49- value = tostring (prop_value)
50- }
51- ]
52- : []
73+ # Only process non-secrets connections here (secrets use envFrom)
74+ ! try (local. is_secrets_resource [conn_name ], false ) &&
75+ try (local. connection_definitions [conn_name ]. disableDefaultEnvVars , false ) != true
76+ ? [
77+ # Add resource properties directly from connection (excluding metadata properties)
78+ for prop_name , prop_value in conn : {
79+ name = upper (" CONNECTION_${ conn_name } _${ prop_name } " )
80+ value = tostring (prop_value)
81+ }
82+ if ! contains (local. excluded_properties , prop_name)
83+ ]
84+ : []
5385 ])
5486
5587 # Replica count - use from properties or default to 1
@@ -99,23 +131,27 @@ locals {
99131 ]
100132
101133 # Environment variables
102- # TODO: Add support for environment variables from Radius secrets resource
103- # When a container references a Radius.Security/secrets resource via connections,
104- # the recipe should populate environment variables from the secret values
105- # stored in the connected Radius secret resource.
134+ # Supports direct values and secretKeyRef for referencing Kubernetes secrets
106135 env = concat (
107136 [
108137 for env_name , env_config in try (config. env , {}) : {
109- name = env_name
110- value = try (env_config. value , null )
111- value_from = try (env_config. valueFrom , null )
112- # TODO: Currently only 'value' is rendered in the deployment.
113- # Add support for 'valueFrom' to reference Kubernetes secrets/configmaps.
138+ name = env_name
139+ value = try (env_config. value , null )
140+ value_from = try (env_config. valueFrom , null ) != null ? {
141+ secret_key_ref = try (env_config. valueFrom . secretKeyRef , null ) != null ? {
142+ name = env_config.valueFrom.secretKeyRef.secretName
143+ key = env_config.valueFrom.secretKeyRef.key
144+ } : null
145+ } : null
114146 }
115147 ],
116148 local. connection_env_vars
117149 )
118150
151+ # Environment variables from secrets (via envFrom.secretRef)
152+ # Injects all keys from secrets connections as environment variables
153+ env_from = local. secrets_env_from
154+
119155 # Volume mounts
120156 volume_mounts = [
121157 for vm in try (config. volumeMounts , []) : {
@@ -164,27 +200,21 @@ locals {
164200 for vol_name , vol_config in local . volumes : {
165201 name = vol_name
166202
167- # Persistent Volume Claim
168- persistent_volume_claim = try (vol_config. persistentVolume , null ) != null ? (
169- try (vol_config. persistentVolume . claimName , " " ) != " " ? {
170- claim_name = vol_config.persistentVolume.claimName
171- } : (
172- try (local. connections [vol_name ]. status . computedValues . claimName , " " ) != " " ? {
173- claim_name = local.connections[vol_name].status.computedValues.claimName
174- } : null
175- )
176- ) : null
177-
178- # Secret
179- secret = try (vol_config. secret , null ) != null ? {
180- secret_name = vol_config.secret.secretName
203+ # Persistent Volume Claim - extract PVC name from resourceId (last segment of the path)
204+ persistent_volume_claim = try (vol_config. persistentVolume , null ) != null ? {
205+ claim_name = element (split (" /" , vol_config. persistentVolume . resourceId ), length (split (" /" , vol_config. persistentVolume . resourceId )) - 1 )
206+ } : null
207+
208+ # Secret volume - use secretName from volume config
209+ secret = try (vol_config. secretName , null ) != null ? {
210+ secret_name = vol_config.secretName
181211 } : null
182212
183213 # EmptyDir
184214 empty_dir = try (vol_config. emptyDir , null ) != null ? {
185215 medium = try (vol_config. emptyDir . medium , null ) != null ? (
186- lower (vol_config. emptyDir . medium ) == " memory" ? " Memory" : lower (vol_config . emptyDir . medium ) == " disk " ? " " : vol_config.emptyDir.medium
187- ) : " "
216+ lower (vol_config. emptyDir . medium ) == " memory" ? " Memory" : " "
217+ ) : null
188218 } : null
189219 }
190220 ]
@@ -260,7 +290,7 @@ resource "kubernetes_deployment" "deployment" {
260290
261291 template {
262292 metadata {
263- labels = local. labels
293+ labels = local. labels
264294 annotations = local. has_dapr_sidecar ? local. dapr_annotations : null
265295 }
266296
@@ -289,7 +319,7 @@ resource "kubernetes_deployment" "deployment" {
289319 }
290320 }
291321
292- # Environment variables
322+ # Environment variables with direct values
293323 dynamic "env" {
294324 for_each = [for e in init_container . value . env : e if e . value != null ]
295325 content {
@@ -298,6 +328,30 @@ resource "kubernetes_deployment" "deployment" {
298328 }
299329 }
300330
331+ # Environment variables with secretKeyRef
332+ dynamic "env" {
333+ for_each = [for e in init_container . value . env : e if e . value == null && try (e. value_from . secret_key_ref , null ) != null ]
334+ content {
335+ name = env. value . name
336+ value_from {
337+ secret_key_ref {
338+ name = env. value . value_from . secret_key_ref . name
339+ key = env. value . value_from . secret_key_ref . key
340+ }
341+ }
342+ }
343+ }
344+
345+ # Environment variables from secrets (via envFrom.secretRef)
346+ dynamic "env_from" {
347+ for_each = init_container. value . env_from
348+ content {
349+ prefix = env_from. value . prefix
350+ secret_ref {
351+ name = env_from. value . name
352+ }
353+ }
354+ }
301355
302356 # Volume mounts
303357 dynamic "volume_mount" {
@@ -350,6 +404,30 @@ resource "kubernetes_deployment" "deployment" {
350404 }
351405 }
352406
407+ # Environment variables with secretKeyRef
408+ dynamic "env" {
409+ for_each = [for e in container . value . env : e if e . value == null && try (e. value_from . secret_key_ref , null ) != null ]
410+ content {
411+ name = env. value . name
412+ value_from {
413+ secret_key_ref {
414+ name = env. value . value_from . secret_key_ref . name
415+ key = env. value . value_from . secret_key_ref . key
416+ }
417+ }
418+ }
419+ }
420+
421+ # Environment variables from secrets (via envFrom.secretRef)
422+ dynamic "env_from" {
423+ for_each = container. value . env_from
424+ content {
425+ prefix = env_from. value . prefix
426+ secret_ref {
427+ name = env_from. value . name
428+ }
429+ }
430+ }
353431
354432 # Volume mounts
355433 dynamic "volume_mount" {
0 commit comments