How-To: Author a Radius Recipe
Categories:
Prerequisites
Before you get started, you’ll need to make sure you have the following tools and resources:
Step 1: Author a Recipe template
Begin by creating your Bicep or Terraform template. Ensure that the resource name(s) are unique and repeatable, so they don’t overlap when used by multiple applications.
To make naming easy, a
context
parameter is automatically injected into your template. It contains metadata about the backing app/environment which you can use to generate unique and repeatable names.
Create app.bicep
and use the context
parameter for naming and namespace configuration:
@description('Radius-provided object containing information about the resource calling the Recipe')
param context object
// Import Kubernetes resources into Bicep
extension kubernetes with {
kubeConfig: ''
namespace: context.runtime.kubernetes.namespace
}
resource redis 'apps/Deployment@v1' = {
metadata: {
// Ensure the resource name is unique and repeatable
name: 'redis-${uniqueString(context.resource.id)}'
}
spec: {
selector: {
matchLabels: {
app: 'redis'
resource: context.resource.name
}
}
template: {
metadata: {
labels: {
app: 'redis'
resource: context.resource.name
}
}
spec: {
containers: [
{
name: 'redis'
image: 'redis:6'
ports: [
{
containerPort: 6379
}
]
}
]
}
}
}
}
resource svc 'core/Service@v1' = {
metadata: {
name: 'redis-${uniqueString(context.resource.id)}'
}
spec: {
type: 'ClusterIP'
selector: {
app: 'redis'
resource: context.resource.name
}
ports: [
{
port: port
targetPort: '6379'
}
]
}
}
Add the context
parameter to your variable.tf
file:
variable "context" {
description = "Radius-provided object containing information about the resource calling the Recipe."
type = any
}
Ensure that your main.tf
has:
- Defined
required_providers
for any providers you leverage in your module. This allows Radius to inject configuration and credentials. - Utilizes the
context
parameter for naming and namespace configuration. This ensures your resources don’t unintentionally collide with other uses of the Recipe.
terraform {
required_providers {
kubernetes = {
source = "hashicorp/kubernetes"
version = ">= 2.0"
}
}
}
resource "kubernetes_deployment" "redis" {
metadata {
name = "redis-${sha512(var.context.resource.id)}"
namespace = var.context.runtime.kubernetes.namespace
labels = {
app = "redis"
}
}
spec {
selector {
match_labels = {
app = "redis"
resource = var.context.resource.name
}
}
template {
metadata {
labels = {
app = "redis"
resource = var.context.resource.name
}
}
spec {
container {
name = "redis"
image = "redis:6"
port {
container_port = 6379
}
}
}
}
}
}
resource "kubernetes_service" "redis" {
metadata {
name = "redis-${sha512(var.context.resource.id)}"
namespace = var.context.runtime.kubernetes.namespace
}
spec {
type = "ClusterIP"
selector = {
app = "redis"
resource = var.context.resource.name
}
port {
port = var.port
target_port = "6379"
}
}
}
Step 2: Add parameters/variables for Recipe customization (optional)
You can optionally add parameters/variables to your Recipe to allow developers and/or operators to customize the Recipe for an application/environment. Parameters can be set both when a Recipe is added to an environment by an operator, or by a developer when the Recipe is called.
You can create any parameter type supported by Bicep:
@description('The port Redis is offered on. Defaults to 6379.')
param port int = 6379
You can create any variable type supported by Terraform:
variable "port" {
description = "The port Redis is offered on. Defaults to 6379."
type = number
default = 6379
}
Step 3: Output the result
Once you have defined your IaC template you will need to output a result
object with the values, secrets, and resources that the target resource requires. This is how Radius “wires up” the Recipe.
The result
object must include:
values
: The fields that the target resource requires. (username, host, port, etc.)secrets
: The fields that the target resource requires, but should be treated as secrets. (password, connectionString, etc.)resources
: The UCP ID(s) of the resources providing the backing service. Used by UCP to track dependencies and manage deletion.
Resources are populated automatically for Bicep Recipes for any new Azure or AWS resource (Kubernetes coming soon). For now, make sure to include the UCP ID of any Kubernetes resources your Recipe creates.
@description('The result of the Recipe. Must match the target resource\'s schema.')
output result object = {
values: {
host: '${svc.metadata.name}.${svc.metadata.namespace}.svc.cluster.local'
port: svc.spec.ports[0].port
username: ''
}
secrets: {
// Temporarily disable linter until secret outputs are added
#disable-next-line outputs-should-not-contain-secrets
password: ''
}
// UCP IDs for the above Kubernetes resources
resources: [
'/planes/kubernetes/local/namespaces/${svc.metadata.namespace}/providers/core/Service/${svc.metadata.name}'
'/planes/kubernetes/local/namespaces/${redis.metadata.namespace}/providers/apps/Deployment/${redis.metadata.name}'
]
}
output "result" {
value = {
values = {
host = "${kubernetes_service.metadata.name}.${kubernetes_service.metadata.namespace}.svc.cluster.local"
port = kubernetes_service.spec.port[0].port
username = ""
}
secrets = {
password = ""
}
// UCP resource IDs
resources = [
"/planes/kubernetes/local/namespaces/${kubernetes_service.metadata.namespace}/providers/core/Service/${kubernetes_service.metadata.name}",
"/planes/kubernetes/local/namespaces/${kubernetes_deployment.metadata.namespace}/providers/apps/Deployment/${kubernetes_deployment.metadata.name}"
]
}
description = "The result of the Recipe. Must match the target resource's schema."
sensitive = true
}
Step 4: Store your template
Recipes leverage Bicep registries for template storage. Once you’ve authored a Recipe, you can publish it to your preferred OCI-compliant registry with rad bicep publish
:
rad bicep publish --file myrecipe.bicep --target br:ghcr.io/USERNAME/recipes/myrecipe:1.1.0
Follow the Terraform module publishing docs to setup and publish a Terraform module to a Terraform registry.
Step 5: Register your Recipe with your environment
Now that your Recipe template has been stored, you can add it your Radius Environment to be used by developers. This allows you to mix-and-match templates for each of your environments such as dev, canary, and prod.
rad recipe register myrecipe --environment myenv --resource-type Applications.Datastores/redisCaches --template-kind bicep --template-path ghcr.io/USERNAME/recipes/myrecipe:1.1.0
The template path value should represent the source path found in your Terraform module registry.
rad recipe register myrecipe --environment myenv --resource-type Applications.Datastores/redisCaches --template-kind terraform --template-path user/recipes/myrecipe --template-version "1.1.0"
extension radius
resource env 'Applications.Core/environments@2023-10-01-preview' = {
name: 'prod'
properties: {
compute: {
kind: 'kubernetes'
resourceId: 'self'
namespace: 'default'
}
recipes: {
'Applications.Datastores/redisCaches':{
'redis-bicep': {
templateKind: 'bicep'
templatePath: 'https://ghcr.io/USERNAME/recipes/myrecipe:1.1.0'
// Optionally set parameters for all resources calling this Recipe
parameters: {
port: 3000
}
}
'redis-terraform': {
templateKind: 'terraform'
templatePath: 'user/recipes/myrecipe'
templateVersion: '1.1.0'
// Optionally set parameters for all resources calling this Recipe
parameters: {
port: 3000
}
}
}
}
}
}
Step 6 : Use the custom recipe in your application
You can now use your custom Recipe with its accompanying resource in your application.
Note that if your Recipe is registered with the name “default”, you do not need to provide a Recipe name in your application, as it will automatically pick up the default Recipe.
resource redis 'Applications.Datastores/redisCaches@2023-10-01-preview'= {
name: 'myresource'
properties: {
environment: environment
application: application
recipe: {
name: 'myrecipe'
}
}
}
Further reading
Feedback
Was this page helpful?
Glad to hear it! Please feel free to star our repo and join our Discord server to stay up to date with the project.
Sorry to hear that. If you would like to also contribute a suggestion visit and tell us how we can improve.