Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,16 @@ resource svc 'core/Service@v1' = {
}

//////////////////////////////////////////
// Output Radius result
// Output Radius result
//
// No `secrets` output: the administrator credentials flow *into* this Recipe as
// user-supplied properties, so there is nothing for the Recipe to hand back. An
// application that needs the password for a consuming container already holds it
// and should author its own Radius.Security/secrets resource, binding it with
// `valueFrom.secretKeyRef` (see test/app.bicep). This matches the Azure recipe in
// recipe-packs/azure/aks-recipepack.bicep, which maps only `host`. Contrast with
// redisCaches, where the access key is generated by the infrastructure and so is
// returned via `outputs.secrets`.
Comment on lines +196 to +200
//////////////////////////////////////////

output result object = {
Expand All @@ -204,11 +213,8 @@ output result object = {
)
values: {
host: '${svc.metadata.name}.${svc.metadata.namespace}.svc.cluster.local'
port: port
// `port` is declared `type: string` on the Resource Type, so emit a string.
port: string(port)
database: database
}
secrets: {
password: password
connectionString: 'postgresql://${username}:${password}@${svc.metadata.name}.${svc.metadata.namespace}.svc.cluster.local:${port}/${database}'
}
}
33 changes: 15 additions & 18 deletions Data/postgreSqlDatabases/recipes/kubernetes/terraform/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ terraform {

variable "context" {
description = "This variable contains Radius Recipe context."
type = any
type = any
}

variable "memory" {
Expand All @@ -32,17 +32,17 @@ variable "memory" {
}

locals {
resource_name = var.context.resource.name
application_name = var.context.application != null ? var.context.application.name : ""
environment_name = var.context.environment != null ? var.context.environment.name : ""
resource_group = element(split("/", var.context.resource.id), 5)
namespace = var.context.runtime.kubernetes.namespace
port = 5432
tag = "16-alpine"
username = var.context.resource.properties.username
password = var.context.resource.properties.password
database = try(var.context.resource.properties.database, "postgres_db")
size_value = try(var.context.resource.properties.size, "S")
resource_name = var.context.resource.name
application_name = var.context.application != null ? var.context.application.name : ""
environment_name = var.context.environment != null ? var.context.environment.name : ""
resource_group = element(split("/", var.context.resource.id), 5)
namespace = var.context.runtime.kubernetes.namespace
port = 5432
tag = "16-alpine"
username = var.context.resource.properties.username
password = var.context.resource.properties.password
database = try(var.context.resource.properties.database, "postgres_db")
size_value = try(var.context.resource.properties.size, "S")

labels = {
"radapp.io/resource" = local.resource_name
Expand Down Expand Up @@ -150,6 +150,8 @@ resource "kubernetes_service" "postgres" {
}
}

# The administrator credentials are user-supplied Recipe inputs. They configure
# PostgreSQL through the Kubernetes Secret above but are not Recipe outputs.
output "result" {
value = {
resources = [
Expand All @@ -159,13 +161,8 @@ output "result" {
]
values = {
host = "${kubernetes_service.postgres.metadata[0].name}.${kubernetes_service.postgres.metadata[0].namespace}.svc.cluster.local"
port = local.port
port = tostring(local.port)
database = local.database
}
secrets = {
password = local.password
connectionString = "postgresql://${local.username}:${local.password}@${kubernetes_service.postgres.metadata[0].name}.${kubernetes_service.postgres.metadata[0].namespace}.svc.cluster.local:${local.port}/${local.database}"
}
}
sensitive = true
}
44 changes: 41 additions & 3 deletions Data/postgreSqlDatabases/test/app.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,37 @@ extension radius
@description('The ID of your Radius Environment. Set automatically by the rad CLI.')
param environment string

@description('Database admin password. Set on the resource `password` property (x-radius-sensitive), so Radius encrypts it at rest and injects it decrypted into the Recipe as the flexible server administrator password.')
@description('Database admin password. Set on the `password` property of the database (x-radius-sensitive, so Radius encrypts it at rest and injects it decrypted into the Recipe) and stored in a Radius.Security/secrets resource for the consuming container to bind by reference.')
@secure()
param password string

resource app 'Radius.Core/applications@2025-08-01-preview' = {
name: 'postgresql-azure-test'
name: 'postgresql-test'
properties: {
environment: environment
}
}

// The password is also needed by the consuming container. Store it in a
// Radius.Security/secrets resource rather than passing it to the container as a
// plain `env` value: `data.value` is x-radius-sensitive (encrypted at rest,
// redacted on reads), whereas a container `env.value` is stored unencrypted on
// the container resource and rendered literally into the Pod spec.
resource dbCreds 'Radius.Security/secrets@2025-08-01-preview' = {
// Keep this distinct from the Recipe-owned `postgresql-credentials`
// Kubernetes Secret that configures the PostgreSQL container.
name: 'postgresql-client-credentials'
properties: {
environment: environment
application: app.id
data: {
password: {
value: password
}
}
}
}

resource postgresql 'Radius.Data/postgreSqlDatabases@2025-08-01-preview' = {
name: 'postgresql'
properties: {
Expand All @@ -34,6 +54,22 @@ resource democontainer 'Radius.Compute/containers@2025-08-01-preview' = {
containers: {
demo: {
image: 'ghcr.io/radius-project/samples/demo:latest'
// Host, port, username, and database arrive automatically as
// CONNECTION_POSTGRESQL_* env vars from the connection below. The
// connection cannot carry the password (x-radius-sensitive properties
// redact to null on reads and are skipped), so it is bound by reference
// here under the same naming scheme, filling the one gap the connection
// leaves. The value never lands in the Pod spec or on this container.
env: {
CONNECTION_POSTGRESQL_PASSWORD: {
valueFrom: {
secretKeyRef: {
secretName: dbCreds.name
key: 'password'
}
}
}
}
ports: {
web: {
containerPort: 3000
Expand All @@ -42,7 +78,9 @@ resource democontainer 'Radius.Compute/containers@2025-08-01-preview' = {
}
}
connections: {
postgres: {
// Named `postgresql` so the injected variables are CONNECTION_POSTGRESQL_*,
// which is the prefix the demo image looks for.
postgresql: {
source: postgresql.id
}
}
Expand Down