From fb24859025d2404f1315903599a036d1826f8985 Mon Sep 17 00:00:00 2001 From: Dan Rios <36534747+riosengineer@users.noreply.github.com> Date: Mon, 2 Jun 2025 16:29:34 +0000 Subject: [PATCH 1/3] Add Azure Files and Storage Bicep examples with parameter files --- .../fail-function/azure-files.bicep | 34 +++++++++++++++++++ .../fail-function/azure-files.bicepparam | 4 +++ bicep-examples/fail-function/storage.bicep | 20 +++++++++++ .../fail-function/storage.bicepparam | 3 ++ 4 files changed, 61 insertions(+) create mode 100644 bicep-examples/fail-function/azure-files.bicep create mode 100644 bicep-examples/fail-function/azure-files.bicepparam create mode 100644 bicep-examples/fail-function/storage.bicep create mode 100644 bicep-examples/fail-function/storage.bicepparam diff --git a/bicep-examples/fail-function/azure-files.bicep b/bicep-examples/fail-function/azure-files.bicep new file mode 100644 index 0000000..199ed1f --- /dev/null +++ b/bicep-examples/fail-function/azure-files.bicep @@ -0,0 +1,34 @@ +@description('Name of the storage account.') +param storageAccountName string + +@description('Azure resource location.') +param location string = resourceGroup().location + +@description('Enable Azure AD (Entra ID) authentication for Azure Files?') +param enableAzureAD bool + +var entraIdCheck = enableAzureAD ? true : fail('Azure AD (Entra ID) must be enabled for Azure Files to work properly. Set enableAzureAD to true.') + +resource resStorageAccount 'Microsoft.Storage/storageAccounts@2024-01-01' = { + name: entraIdCheck ? storageAccountName : storageAccountName + location: location + sku: { + name: 'Standard_LRS' + } + kind: 'StorageV2' + properties: { + + } +} + +resource resFileService 'Microsoft.Storage/storageAccounts/fileServices@2024-01-01' = { + name: 'default' + parent: resStorageAccount +} + +resource resFileShare 'Microsoft.Storage/storageAccounts/fileServices/shares@2024-01-01' = { + name: 'entraFileShare' + parent: resFileService + properties: { + } +} diff --git a/bicep-examples/fail-function/azure-files.bicepparam b/bicep-examples/fail-function/azure-files.bicepparam new file mode 100644 index 0000000..0edc457 --- /dev/null +++ b/bicep-examples/fail-function/azure-files.bicepparam @@ -0,0 +1,4 @@ +using './azure-files.bicep' + +param storageAccountName = '' +param enableAzureAD = false diff --git a/bicep-examples/fail-function/storage.bicep b/bicep-examples/fail-function/storage.bicep new file mode 100644 index 0000000..eb02f36 --- /dev/null +++ b/bicep-examples/fail-function/storage.bicep @@ -0,0 +1,20 @@ +@description('Name of the storage account - without st prefix. ') +param storageAccountName string + +@description('Azure resource location.') +param location string = resourceGroup().location + +var storageAccountPrefix = 'st' +var storageAccountNameChecked = startsWith(storageAccountName, storageAccountPrefix) + ? storageAccountName + : fail('The storage account name must start with "${storageAccountPrefix}".') + +resource resStorageAccount 'Microsoft.Storage/storageAccounts@2022-09-01' = { + name: storageAccountNameChecked + location: location + sku: { + name: 'Standard_LRS' + } + kind: 'StorageV2' + properties: {} +} diff --git a/bicep-examples/fail-function/storage.bicepparam b/bicep-examples/fail-function/storage.bicepparam new file mode 100644 index 0000000..b97f9ee --- /dev/null +++ b/bicep-examples/fail-function/storage.bicepparam @@ -0,0 +1,3 @@ +using './main.bicep' + +param storageAccountName = 'blobstorageaccount001' From ec5fb3fe44a3f8953655ce707bbd890ede78bca4 Mon Sep 17 00:00:00 2001 From: Dan Rios <36534747+riosengineer@users.noreply.github.com> Date: Tue, 3 Jun 2025 15:19:30 +0000 Subject: [PATCH 2/3] add new Web App Bicep example with runtime validation --- bicep-examples/fail-function/README.md | 58 +++++++++++++++++++ .../fail-function/azure-files.bicep | 34 ----------- .../fail-function/azure-files.bicepparam | 4 -- bicep-examples/fail-function/webApp.bicep | 37 ++++++++++++ .../fail-function/webApp.bicepparam | 3 + 5 files changed, 98 insertions(+), 38 deletions(-) create mode 100644 bicep-examples/fail-function/README.md delete mode 100644 bicep-examples/fail-function/azure-files.bicep delete mode 100644 bicep-examples/fail-function/azure-files.bicepparam create mode 100644 bicep-examples/fail-function/webApp.bicep create mode 100644 bicep-examples/fail-function/webApp.bicepparam diff --git a/bicep-examples/fail-function/README.md b/bicep-examples/fail-function/README.md new file mode 100644 index 0000000..deaeaca --- /dev/null +++ b/bicep-examples/fail-function/README.md @@ -0,0 +1,58 @@ +# Azure Bicep - Fail Function + +## Introduction + +The `fail()` function in Bicep allows you to stop deployments with custom error messages when conditions are not met. This is useful for enforcing business rules, validating parameters, and ensuring proper configuration before resources are deployed. + +## 📃 Benefits of the Fail Function + +✅ **Early Validation**: Catches configuration errors before Azure resources are created. + +✅ **Clear Error Messages**: Provides custom, descriptive error messages. + +✅ **Enforces Standards**: Ensures naming conventions and business rules are followed consistently. + +✅ **Prevents Runtime Issues**: Stops deployments that would fail to function properly. + +## Examples + +### Storage Account Naming Convention + +Ensures storage account names start with "st" prefix. The fail function here is populating the `storageAccountNameChecked` var with our fail condition, if the `storageAccountName` value does not start with the value from `storageAccountPrefix` then it will fail pre-deployment. + +```bicep +var storageAccountPrefix = 'st' +var storageAccountNameChecked = startsWith(storageAccountName, storageAccountPrefix) + ? storageAccountName + : fail('The storage account name must start with "${storageAccountPrefix}".') +``` + +### Web App Runtime Validation + +Ensures runtime stack is specified. With Web Apps, you can technically deploy them without any runtime specified and ARM will return a successful deployment. However, the web app will not be functional as the runtime and stack are not set (e.g. DOTNET version 9.0). By adding a fail function to check if the parameter is set, you can fail pre-deployment with your custom error message. + +```bicep +var varRuntime = empty(trim(runtime)) ? fail('The runtime parameter must not be empty.') : runtime +``` + +## 🚀 Deployment + +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 './storage.bicep' -p 'storage.bicepparam' +// amend to webApp.bicep / webApp.bicepparam to test that example +``` + +or PowerShell + +```powershell +Connect-AzAccount +Set-AzContext -Subscription "subscription name or id" +New-AzResourceGroupDeployment -Confirm -ResourceGroup "your-rg" -TemplateFile "storage.bicep" -TemplateParameterFile "storage.bicepparam" +// amend to webApp.bicep / webApp.bicepparam to test that example +``` \ No newline at end of file diff --git a/bicep-examples/fail-function/azure-files.bicep b/bicep-examples/fail-function/azure-files.bicep deleted file mode 100644 index 199ed1f..0000000 --- a/bicep-examples/fail-function/azure-files.bicep +++ /dev/null @@ -1,34 +0,0 @@ -@description('Name of the storage account.') -param storageAccountName string - -@description('Azure resource location.') -param location string = resourceGroup().location - -@description('Enable Azure AD (Entra ID) authentication for Azure Files?') -param enableAzureAD bool - -var entraIdCheck = enableAzureAD ? true : fail('Azure AD (Entra ID) must be enabled for Azure Files to work properly. Set enableAzureAD to true.') - -resource resStorageAccount 'Microsoft.Storage/storageAccounts@2024-01-01' = { - name: entraIdCheck ? storageAccountName : storageAccountName - location: location - sku: { - name: 'Standard_LRS' - } - kind: 'StorageV2' - properties: { - - } -} - -resource resFileService 'Microsoft.Storage/storageAccounts/fileServices@2024-01-01' = { - name: 'default' - parent: resStorageAccount -} - -resource resFileShare 'Microsoft.Storage/storageAccounts/fileServices/shares@2024-01-01' = { - name: 'entraFileShare' - parent: resFileService - properties: { - } -} diff --git a/bicep-examples/fail-function/azure-files.bicepparam b/bicep-examples/fail-function/azure-files.bicepparam deleted file mode 100644 index 0edc457..0000000 --- a/bicep-examples/fail-function/azure-files.bicepparam +++ /dev/null @@ -1,4 +0,0 @@ -using './azure-files.bicep' - -param storageAccountName = '' -param enableAzureAD = false diff --git a/bicep-examples/fail-function/webApp.bicep b/bicep-examples/fail-function/webApp.bicep new file mode 100644 index 0000000..37b0f0f --- /dev/null +++ b/bicep-examples/fail-function/webApp.bicep @@ -0,0 +1,37 @@ +@description('The name of the App Service Plan') +param appServicePlanName string = uniqueString(resourceGroup().id) + +@description('The name of the Web App') +param webAppName string = uniqueString(resourceGroup().id) + +// Fail the deployment if the runtime is not specified +@description('The runtime stack for the Web App, e.g. "DOTNET|9.0"') +param runtime string = '' + +// Fail if runtime is empty +var varRuntime = empty(trim(runtime)) ? fail('The runtime parameter is empty!') : runtime + +resource appServicePlan 'Microsoft.Web/serverfarms@2024-11-01' = { + name: appServicePlanName + location: resourceGroup().location + sku: { + name: 'F1' + tier: 'Free' + } + properties: { + reserved: true + } +} + +resource webApp 'Microsoft.Web/sites@2024-11-01' = { + name: webAppName + location: resourceGroup().location + kind: 'app' + properties: { + serverFarmId: appServicePlan.id + httpsOnly: true + siteConfig: { + linuxFxVersion: varRuntime + } + } +} diff --git a/bicep-examples/fail-function/webApp.bicepparam b/bicep-examples/fail-function/webApp.bicepparam new file mode 100644 index 0000000..46b2a5b --- /dev/null +++ b/bicep-examples/fail-function/webApp.bicepparam @@ -0,0 +1,3 @@ +using './webApp.bicep' + +//param runtime = 'DOTNETCORE|9.0' From 42c074b97e8cbfdb0a5c45a86862b56a3847d4ed Mon Sep 17 00:00:00 2001 From: Dan Rios <36534747+riosengineer@users.noreply.github.com> Date: Thu, 5 Jun 2025 15:40:45 +0000 Subject: [PATCH 3/3] Update README and parameter files to enhance runtime validation for Web Apps --- bicep-examples/fail-function/README.md | 4 ++-- bicep-examples/fail-function/storage.bicepparam | 2 +- bicep-examples/fail-function/webApp.bicep | 4 ++-- bicep-examples/fail-function/webApp.bicepparam | 3 ++- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/bicep-examples/fail-function/README.md b/bicep-examples/fail-function/README.md index deaeaca..fc0444e 100644 --- a/bicep-examples/fail-function/README.md +++ b/bicep-examples/fail-function/README.md @@ -29,10 +29,10 @@ var storageAccountNameChecked = startsWith(storageAccountName, storageAccountPre ### Web App Runtime Validation -Ensures runtime stack is specified. With Web Apps, you can technically deploy them without any runtime specified and ARM will return a successful deployment. However, the web app will not be functional as the runtime and stack are not set (e.g. DOTNET version 9.0). By adding a fail function to check if the parameter is set, you can fail pre-deployment with your custom error message. +Ensures that the DOTNET runtime stack is specified. In this example, the `fail` function checks whether the `runtime` parameter includes "DOTNET" by using the `contains` function. If "DOTNET" is not found in the `runtime` value, the deployment will be stopped before any resources are created, displaying your custom error message. ```bicep -var varRuntime = empty(trim(runtime)) ? fail('The runtime parameter must not be empty.') : runtime +var varRuntime = contains(toUpper(runtime), 'DOTNET') ? runtime : fail('The runtime parameter must contain "DOTNET"!') ``` ## 🚀 Deployment diff --git a/bicep-examples/fail-function/storage.bicepparam b/bicep-examples/fail-function/storage.bicepparam index b97f9ee..f69ab24 100644 --- a/bicep-examples/fail-function/storage.bicepparam +++ b/bicep-examples/fail-function/storage.bicepparam @@ -1,3 +1,3 @@ -using './main.bicep' +using './storage.bicep' param storageAccountName = 'blobstorageaccount001' diff --git a/bicep-examples/fail-function/webApp.bicep b/bicep-examples/fail-function/webApp.bicep index 37b0f0f..eea140f 100644 --- a/bicep-examples/fail-function/webApp.bicep +++ b/bicep-examples/fail-function/webApp.bicep @@ -8,8 +8,8 @@ param webAppName string = uniqueString(resourceGroup().id) @description('The runtime stack for the Web App, e.g. "DOTNET|9.0"') param runtime string = '' -// Fail if runtime is empty -var varRuntime = empty(trim(runtime)) ? fail('The runtime parameter is empty!') : runtime +// Fail if runtime does not contain 'DOTNET' +var varRuntime = contains(toUpper(runtime), 'DOTNET') ? runtime : fail('The runtime parameter must contain "DOTNET"!') resource appServicePlan 'Microsoft.Web/serverfarms@2024-11-01' = { name: appServicePlanName diff --git a/bicep-examples/fail-function/webApp.bicepparam b/bicep-examples/fail-function/webApp.bicepparam index 46b2a5b..819994c 100644 --- a/bicep-examples/fail-function/webApp.bicepparam +++ b/bicep-examples/fail-function/webApp.bicepparam @@ -1,3 +1,4 @@ using './webApp.bicep' -//param runtime = 'DOTNETCORE|9.0' +//change to param runtime = 'DOTNETCORE|9.0' to pass fail function +param runtime = 'PYTHON|3.11'