Blog Azure Infrastructuur Security & Compliance Cloud native

7 veelgemaakte beveiligingsfouten in Azure Bicep en wat je wél moet doen

Azure Bicep is de standaard geworden voor het implementeren van Infrastructure as Code (IaC) in Azure en vervangt de meer ingewikkelde Azure Resource Manager (ARM)-templates.

In dit artikel worden zeven veelvoorkomende beveiligingsfouten beschreven die we zien bij het gebruik van Bicep. 

Niels Kroeze

Auteur

Niels Kroeze

Leestijd 6 minuten Gepubliceerd: 19 november 2025

7 Security mistakes you should avoid 

When it comes to using Azure Bicep securely, we recommend you to avoid the following mistakes: 

  1. Leaving secrets in source control 
  2. Not securing inputs properly 
  3. Exposing secrets in outputs 
  4. Weak or default naming conventions 
  5. Not validating parameters  
  6. Over-permissive role assignments 
  7. Skipping policy enforcement 

 

1. Leaving secrets in source control 

One of the most obvious mistakes in Bicep is accidentally committing secrets, such as passwords or access keys, straight into source control. This exposes credentials to anyone with access to the repository, which we all know should be avoided. However, it’s very easy to leave them in, in particularly when testing your Bicep configurations locally. 

To avoid this, never hardcode secrets in your Bicep files. Instead: 

  • Pass parameters in through a command line 
  • Use a separate parameters JSON file and add it to .gitignore to stop Git from tracking it in future commits. Keep in mind this doesn’t remove any secrets already committed as you’ll need to delete them from history and rotate those credentials.
Iac Whitepaper

Meer weten over Infrastructure as Code (IaC) in Azure?

Lees onze nieuwste whitepaper over IaC in Azure en leer alles over Azure Bicep, Azure Verified Modules (AVM) en meer!

Download de IaC whitepaper hier!

2. Inputs niet goed beveiligen

Gevoelige inputs, zoals admin credentials, moeten altijd voorzichtig behandeld worden. Zonder goede bescherming kunnen geheimen in logs terechtkomen of per ongeluk zichtbaar worden. Bicep biedt de @secure()-decorator voor string- en objectparameters om dit te voorkomen.

Bijvoorbeeld:

@secure()
param adminPassword string

@secure()
param adminCredentials object

Met deze decorators verberg je gevoelige inputs in outputs en deploymentlogs.

 

3. Geheimen blootgeven in outputs

Outputs zijn handig om waarden tussen Bicep-modules door te geven. Ze kunnen ook risico’s geven als ze gevoelige data bevatten. Een bekend voorbeeld is het direct outputten van een connection string. Bicep toont dan een waarschuwing dat je een geheim kunt lekken, zoals bij een storage account:

output connectionString = 'DefaultEndpointsProtocol=https;AccountName=${storageaccount.name};EndpointSuffix=${environment().suffixes.storage};AccountKey=${listKeys(storageaccount.id, storageaccount.apiVersion).keys[0].value}'

Die waarschuwing helpt, maar ziet niet alles. Als je de connection string eerst in een variabele zet en daarna die variabele output, verdwijnt de waarschuwing terwijl je alsnog een geheim in plain text output:

var connectionString = 'DefaultEndpointsProtocol=https;AccountName=${storageaccount.name};EndpointSuffix=${environment().suffixes.storage};AccountKey=${listKeys(storageaccount.id, storageaccount.apiVersion).keys[0].value}'

output connectionString = connectionString

Neem dit voorbeeld van een storage account-deployment:

deploy.bicep
param location string = resourceGroup().location
param tags object = {}
param storageName string = 'stsecureteststore'
param sku string = 'Standard_LRS'

module storageModule 'modules/storage.bicep' = {
  name: 'StorageDeploy'
  params: {
    location: location
    storageName: storageName
    tags: tags
    sku: sku
  }
}

modules/storage.bicep

@description('The storage account name')
@minLength(3)
@maxLength(24)
param storageName string
@description('The storage account location')
param location string
@description('The tags for the storage account')
param tags object
@description('The storage account sku')
@allowed(['Standard_LRS', 'Standard_GRS', 'Standard_GZRS', 'Standard_RAGRS', 'Standard_RAGZRS', 'Standard_ZRS', 'Premium_LRS', 'Premium_ZRS'])
param sku string = 'Standard_LRS'
@description('The access tier for the blob services')
@allowed(['Hot', 'Cool'])
param accessTier string = 'Hot'
@description('Allow public access to blobs')
param allowBlobPublicAccess bool = false

resource storageaccount 'Microsoft.Storage/storageAccounts@2025-06-01' = {
  name: storageName
  location: location
  kind: 'StorageV2'
  tags: tags
  sku: {
    name: sku
  }
  properties: {
    supportsHttpsTrafficOnly: true
    minimumTlsVersion: 'TLS1_2'
    accessTier: accessTier
    allowBlobPublicAccess: allowBlobPublicAccess
  }
}

var connectionString = 'DefaultEndpointsProtocol=https;AccountName=${storageaccount.name};EndpointSuffix=${environment().suffixes.storage};AccountKey=${listKeys(storageaccount.id, storageaccount.apiVersion).keys[0].value}'

Wanneer je deze deployment draait, wordt de connectionString-output zichtbaar onder Deployments in de Azure Portal.

Omdat de account key hier in plain text staat, kan iedereen met permissies om deployments te bekijken deze gevoelige data zien. Dit maakt het onnodig risicovol.

De beste aanpak is om geen geheimen in outputs te zetten en outputs te beperken tot niet-gevoelige waarden, zoals resourcenamen of IDs.

Gebruik liever Azure Key Vault en check outputs goed tijdens het ontwerp van je templates. Bicep ondersteunt het veilig ophalen van secrets uit Key Vault en je kunt ze veilig meegeven aan modules zonder risico op lekken.

Let op:

De Key Vault moet worden geconfigureerd om toegang via Azure Resource Manager voor template deployment mogelijk te maken.

Let op: de getSecret-methode werkt alleen als je ’m koppelt aan een module-parameter met de @secure-decorator.

deploy.bicep 
param location string = resourceGroup().location 
param tags object 
param sqlServerName string 
param keyVaultName string 
param keyVaultResourceGroupName string 
param subscriptionId string = subscription().subscriptionId 

resource vaultResource ' Microsoft.KeyVault/vaults@2025-05-01' existing = { 
  name: keyVaultName  
  scope: resourceGroup(subscriptionId, keyVaultResourceGroupName) 
} 

module sqlModule 'modules/sql.bicep' = { 
  name: 'SqlDeploy' 
  params: { 
    location: location 
    tags: tags 
    sqlServerName: sqlServerName 
    administratorLogin: vaultResource.getSecret('sqlUser') 
    administratorLoginPassword: vaultResource.getSecret('sqlPassword') 
  }   
} 

modules/sql.bicep 
@description('The resource location') 
param location string 
@description('The tags for the resources') 
param tags object 
@description('The name for the SQL Server') 
param sqlServerName string 
@secure() 
@description('The SQL Administrator Login') 
param administratorLogin string 
@secure() 
@description('The SQL Administrator password') 
param administratorLoginPassword string 

resource sqlServerResource 'Microsoft.Sql/servers/databases/extensions@2024-11-01-preview' = { 
  name: sqlServerName 
  location: location 
  tags: tags 
  properties: { 
    administratorLogin: administratorLogin 
    administratorLoginPassword: administratorLoginPassword 
  } 
} 

Op deze manier blijven secrets in Key Vault en komen ze niet in outputs of logs terecht.

 

4. Zwakke of standaard naamgevingsconventies

Een ander veelvoorkomend probleem is het gebruiken van voorspelbare namen van resources. Net zoals simpele wachtwoorden te makkelijk te raden zijn, geven namen zoals teststorage of productionvm te veel weg over je omgeving.

Voorbeeld:

resource storageaccount 'Microsoft.Storage/storageAccounts@2025-06-01' = { 
  name: 'prodstorage' 
  ... 
} 

Beter: gebruik parameters met een willekeurige suffix of een unieke ID:

param storageName string = 'st${uniqueString(resourceGroup().id)}' 

resource storageaccount 'Microsoft.Storage/storageAccounts@2025-06-01' = { 
  name: storageName 
  ... 
} 

 

5. Parameters niet valideren

Te brede parameterinputs leveren snel fouten, typos of onveilige configuraties op.

Voorbeeld (te open):

param sku string 

Beter: gebruik @allowed of andere validatie.

@allowed(['Standard_LRS', 'Standard_ZRS', 'Premium_LRS']) 
param sku string = 'Standard_LRS' 

 

6. Te brede role assignments

Rollen zoals Owner of Contributor toekennen terwijl dat niet nodig is, vergroot de kans op fouten of misbruik.

Voorbeeld:

resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { 
  name: guid(resourceGroup().id, principalId, roleDefinitionId) 
  properties: { 
    principalId: principalId 
    roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') // Contributor 
  } 
} 

Beter: kies de kleinste rol die nodig is, zoals Storage Blob Data Reader voor alleen leesrechten.

 

7. Policies overslaan

Als je zonder Azure Policy of templatevalidatie deployt, mis je belangrijke controles. Daardoor glippen configuraties als open storage-accounts of publieke IP’s sneller door.

Voorbeeld: een storage account met allowBlobPublicAccess = true.

param allowBlobPublicAccess bool = true 

Beter: dwing een policy af op subscription- of management group-niveau. Je kunt ook veilige defaults gebruiken:

param allowBlobPublicAccess bool = false 
Let op:

Bij Intercept raden we altijd aan om openbare toegang, "public blob-access" in Azure Storage, te vermijden om onbedoelde blootstelling van gegevens te voorkomen. Doe dit alleen als er een heel specifieke reden is. 

Conclusie

Deze fouten komen vaak voor, maar met de juiste werkwijzen die hier worden beschreven, zijn ze vrij eenvoudig te voorkomen. Door secrets veilig te beheren, inputs te beschermen, outputs te controleren en sterk governance toe te passen, ben je al goed op weg om te voorkomen dat verkeerde configuraties tot ernstige problemen leiden. 

Deni visual

Neem contact met ons op!

Laten we onze krachten bundelen tijdens jouw cloud reis.