Let’s face it — hardcoding passwords into templates is like taping your house key to the front door. This exercise shows how to do it the secure way.
🚀 What We're Building
You're now upgrading your Bicep deployment from “just works” to “production-friendly.” That means:
- No more hardcoding sensitive values
- Centralizing secrets in Azure Key Vault
- Using parameter files to stay clean, consistent, and secure across environments
In short:
We’re teaching our Bicep template to “read the room” — or at least read the values — from a secure source 🎯
🛠 Step 1: Prep Your Template (Bicep File)
🧹 First, remove the default SKU from your appServicePlanSku
@description('The name and tier of the App Service plan SKU.')
param appServicePlanSku object
Why? Because we’ll define it in a parameter file (great for dev/test/prod flexibility).
✍️ Step 2: Add Secure Parameters
Next, let’s add two new secure parameters for a SQL Server:
@secure()
@description('The administrator login username for the SQL server.')
param sqlServerAdministratorLogin string
@secure()
@description('The administrator login password for the SQL server.')
param sqlServerAdministratorPassword string
And finally, one for the database SKU:
@description('The name and tier of the SQL database SKU.')
param sqlDatabaseSku object
⚠️ Pro Tip:
We don’t set default values for passwords or usernames. We want these provided securely.
🧮 Step 3: Add Variables
These help us build consistent names dynamically:
var appServicePlanName = '${environmentName}-${solutionName}-plan'
var appServiceAppName = '${environmentName}-${solutionName}-app'
var sqlServerName = '${environmentName}-${solutionName}-sql'
var sqlDatabaseName = 'Employees'
🧱 Step 4: Add the SQL Resources
At the bottom of your main.bicep
, define the SQL server and DB:
resource sqlServer 'Microsoft.Sql/servers@2024-05-01-preview' = {
name: sqlServerName
location: location
properties: {
administratorLogin: sqlServerAdministratorLogin
administratorLoginPassword: sqlServerAdministratorPassword
}
}
resource sqlDatabase 'Microsoft.Sql/servers/databases@2024-05-01-preview' = {
parent: sqlServer
name: sqlDatabaseName
location: location
sku: {
name: sqlDatabaseSku.name
tier: sqlDatabaseSku.tier
}
}
📁 Step 5: Create a Parameter File
Now let’s offload the actual values into a file.
🗂 Create: main.parameters.dev.json
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"appServicePlanSku": {
"value": {
"name": "F1",
"tier": "Free"
}
},
"sqlDatabaseSku": {
"value": {
"name": "Standard",
"tier": "Standard"
}
}
}
}
Save it. 🎉
🔑 Step 6: Deploy and Prompt for Sensitive Info
Run:
az login
az group create --name BicepRG --location westus
az deployment group create \
--resource-group BicepRG \
--name main \
--template-file main.bicep \
--parameters main.parameters.dev.json
You’ll be prompted to manually enter:
- SQL Admin Login
- SQL Admin Password
Azure will validate them. Make sure:
- Login is not
admin
,root
, or something basic - Password meets complexity rules
🏰 Step 7: Enter the Castle — Azure Key Vault
Now we create a key vault to hold our secrets 🔐
keyVaultName='your-unique-kv-name'
read -s -p "Enter the login name: " login
read -s -p "Enter the password: " password
az keyvault create \
--name $keyVaultName \
--resource-group BicepRG \
--location westus \
--enabled-for-template-deployment true
⚠️ If you get a “not authorized” error — go to Azure Portal and assign yourself the
Key Vault Secrets Officer
role.
Then, store your secrets:
az keyvault secret set --vault-name $keyVaultName --name "sqlServerAdministratorLogin" --value $login --output none
az keyvault secret set --vault-name $keyVaultName --name "sqlServerAdministratorPassword" --value $password --output none
🧭 Step 8: Get the Key Vault ID
az keyvault show --name $keyVaultName --query id --output tsv
It’ll look like:
/subscriptions/xxx/resourceGroups/BicepRG/providers/Microsoft.KeyVault/vaults/your-kv-name
🔄 Step 9: Update Parameter File with Secret References
Back in main.parameters.dev.json
, append your Key Vault references:
"sqlServerAdministratorLogin": {
"reference": {
"keyVault": {
"id": "YOUR-KEY-VAULT-RESOURCE-ID"
},
"secretName": "sqlServerAdministratorLogin"
}
},
"sqlServerAdministratorPassword": {
"reference": {
"keyVault": {
"id": "YOUR-KEY-VAULT-RESOURCE-ID"
},
"secretName": "sqlServerAdministratorPassword"
}
}
✏️ Be careful with spelling — I accidentally wrote
adminstratiorLogin
and wasted 10 mins wondering what went wrong 😅
🧪 Step 10: Final Deployment (Fully Secure!)
az deployment group create \
--resource-group BicepRG \
--name main \
--template-file main.bicep \
--parameters main.parameters.dev.json
✅ This time, Azure pulls the secrets from the Key Vault — no prompts. Everything is safe, clean, and automated.
🔍 Validate in the Azure Portal
- Go to Resource Groups > BicepRG
- Select the latest Deployment
- Under Inputs, you’ll see all your parameters (except the secure ones — those are hidden)
- Check the SQL Server + Database resources too!
🧠 In Short
Concept | What It Means |
---|---|
Parameter Files | Store your config externally for cleaner templates |
@secure() | Hide sensitive values like passwords from the UI/logs |
Key Vault | Central vault for secrets, with permissions + audit trails |
Reference() | Use Key Vault secrets without exposing them directly |
RBAC Fix | If blocked, assign yourself the Key Vault Secrets Officer role |
Wanna follow my Azure learning journey?
Stick around — I’m sharing it all, wins and stumbles included 😄
You can find me on LinkedIn — drop me a message and just say hi 👋
Would love to hear what you're working on or learning!