Skip to content
Merged
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
78 changes: 78 additions & 0 deletions bicep-examples/validate/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Azure Bicep - Validate Decorator

## Introduction

The `@validate()` decorator in Azure Bicep allows you to add custom validation logic to your parameters. This enables you to enforce complex constraints that go beyond basic type checking, catching configuration errors at deployment time with clear error messages.

This feature was released in [Bicep v0.32](https://github.com/Azure/bicep/releases/tag/v0.32.4) as an experimental feature.

## 📃 Benefits of the Validate Decorator

✅ **Custom Validation**: Define complex validation rules using lambda expressions that return true or false.

✅ **Clear Error Messages**: Validation errors include your custom message, making it easy to understand what went wrong.

✅ **ARM-Level Enforcement**: Validation is enforced by Azure Resource Manager at deployment time, ensuring invalid configurations are rejected before resources are created.

## ⚗️ Enabling the Experimental Feature

The `@validate()` decorator is currently an experimental feature. To enable it, add the following to your `bicepconfig.json` file:

```json
{
"experimentalFeaturesEnabled": {
"userDefinedConstraints": true
}
}
```

## Example

In this example, the `@validate()` decorator uses a lambda expression to enforce that a CORS allowed origin FQDN does not contain protocol prefixes and is a valid domain format. The template then prepends `https://` to the validated FQDN when configuring the Storage Account CORS rules.

```bicep
@description('Allowed origin FQDN for CORS. Must not contain https:// or http:// prefix.')
@validate(
x => !contains(x, 'https://') && !contains(x, 'http://') && contains(x, '.'),
'The allowed origin FQDN must not contain "https://" or "http://" prefix and must be a valid domain name.'
)
param allowedOriginFqdn string
```

The lambda syntax `x => expression` allows you to reference the parameter value within the validation expression. You can combine multiple conditions using logical operators like `&&` (and) and `||` (or).

> [!IMPORTANT]
> The `@validate()` decorator is enforced at **ARM deployment time**, not at Bicep compile time. This means you need to deploy the template to see validation errors. If you want alternative options ahead of ARM engine deployment, look at the Bicep Test Framework and the `fail()` function.

### Validation Error Example

If you were to deploy with an invalid value like `https://app.contoso.com`, ARM would reject the deployment with an error similar to:

```plaintext
{"code": "InvalidTemplate", "message": "Deployment template validation failed: The template parameter 'allowedOriginFqdn' failed validation. The allowed origin FQDN must not contain \"https://\" or \"http://\" prefix and must be a valid domain name."}
```

This enables authors to define meaningful validation messages that surface during deployment, providing clear guidance on how to resolve the issue.

## 🚀 Deployment

> [!NOTE]
> You need to have a resource group deployed before trying this out.

In Visual Studio Code open a terminal and run:

CLI

```bash
az login
az account set --subscription 'subscription name or id'
az deployment group create -g 'your-rg' --confirm-with-what-if -f './main.bicep' -p 'main.bicepparam'
```

or PowerShell

```powershell
Connect-AzAccount
Set-AzContext -Subscription "subscription name or id"
New-AzResourceGroupDeployment -Confirm -ResourceGroup "your-rg" -TemplateFile "main.bicep" -TemplateParameterFile "main.bicepparam"
```
16 changes: 16 additions & 0 deletions bicep-examples/validate/bicepconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
// See https://aka.ms/bicep/config for more information on Bicep configuration options
// Press CTRL+SPACE at any location to see Intellisense suggestions
"analyzers": {
"core": {
"rules": {
"no-unused-params": {
"level": "warning"
}
}
}
},
"experimentalFeaturesEnabled": {
"userDefinedConstraints": true
}
}
61 changes: 61 additions & 0 deletions bicep-examples/validate/main.bicep
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Rios Engineer - Bicep Validate Decorator
targetScope = 'resourceGroup'

// Parameters
@description('Deployment location for resources.')
param location string = resourceGroup().location

// Parameters with validate decorator
@description('Allowed origin FQDN for CORS. Must not contain https:// or http:// prefix.')
@validate(
x => !contains(x, 'https://') && !contains(x, 'http://') && contains(x, '.'),
'The allowed origin FQDN must not contain "https://" or "http://" prefix and must be a valid domain name.'
)
param allowedOriginFqdn string

// Resources
resource storageAccount 'Microsoft.Storage/storageAccounts@2024-01-01' = {
name: 'st${uniqueString(resourceGroup().id)}'
location: location
sku: {
name: 'Standard_LRS'
}
kind: 'StorageV2'
properties: {
minimumTlsVersion: 'TLS1_2'
supportsHttpsTrafficOnly: true
}
}

resource blobService 'Microsoft.Storage/storageAccounts/blobServices@2024-01-01' = {
parent: storageAccount
name: 'default'
properties: {
cors: {
corsRules: [
{
allowedOrigins: [
'https://${allowedOriginFqdn}'
]
allowedMethods: [
'GET'
]
allowedHeaders: [
'*'
]
exposedHeaders: [
'*'
]
maxAgeInSeconds: 3600
}
]
}
}
}

// Outputs
@description('The name of the storage account.')
output storageAccountName string = storageAccount.name

@description('The allowed origin configured for CORS.')
output allowedOrigin string = 'https://${allowedOriginFqdn}'
3 changes: 3 additions & 0 deletions bicep-examples/validate/main.bicepparam
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
using './main.bicep'

param allowedOriginFqdn = 'app.contoso.com'