Converting to Strong Types
Replaces loose object and array parameter types with strong alternatives for compile-time validation and autocompletion.
Conversion Workflow
- •
Identify loose types
- •Search for
param <name> objectandparam <name> arraydeclarations.
- •Search for
- •
Analyze parameter usage
- •Examine how the parameter is used in the template to determine expected structure.
- •
Choose conversion strategy
| Pattern | Strategy |
|---|---|
| Array of primitives | string[], int[], bool[] |
| Array with constrained values | ('value1' | 'value2')[] |
| Object matching resource property | resourceInput<'Type@Version'>.properties.X |
| Custom object structure | User-defined type with type keyword |
| Array of objects | User-defined type with [] suffix |
- •
Define types and update parameters
- •Place type definitions above parameters.
- •
Validate
- •Run command
bicep build <.bicep file> --stdout --no-restoreto verify syntax and type correctness.
- •Run command
Quick Reference
Simple Arrays
// Before param addresses array // After param addresses string[]
Union Types (Constrained Values)
param environments ('dev' | 'staging' | 'prod')[]
param skuName 'Standard_LRS' | 'Premium_LRS'
User-Defined Types
type subnetType = {
name: string
addressPrefix: string
nsgId: string? // Optional property
}
param subnets subnetType[]
Type Best Practices
- •Use
?for optional properties:description: string? - •Use
@sealed()to prevent extra properties at deployment - •Use
@description()on types and properties - •Use constraints:
@minLength(),@maxLength(),@minValue(),@maxValue() - •Compose complex types from simpler types
MCP Tools
| Tool | Purpose |
|---|---|
Bicep:get_bicep_best_practices | Current best practices |
Bicep:get_az_resource_type_schema | Schema for resource-derived types |
Bicep:list_az_resource_types_for_provider | Available types and API versions |
Bicep:list_avm_metadata | Metadata for Azure Verified Modules |
Edge Cases
Dynamic objects (unknown keys): Use wildcard { *: string }
Mixed-type arrays: Use union (string | int | bool)[]
Optionality and backwards compatibility: Use optional types ? for new properties
Resource-Derived Types
Use resourceInput<> and resourceOutput<> to derive types directly from Azure resource schemas. Prioritise this approach over custom user-defined types.
Syntax
resourceInput<'<resourceType>@<apiVersion>'> // Writable properties resourceOutput<'<resourceType>@<apiVersion>'> // Readable properties (includes computed)
When to Use
- •Parameter must match resource property structure exactly
- •Want to avoid maintaining custom types that mirror resource properties
Examples
Full Properties Block
param storageAccountProps resourceInput<'Microsoft.Storage/storageAccounts@2023-01-01'>.properties = {
accessTier: 'Hot'
minimumTlsVersion: 'TLS1_2'
allowBlobPublicAccess: false
supportsHttpsTrafficOnly: true
}
resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = {
name: 'mystorageacct'
location: resourceGroup().location
sku: { name: 'Standard_LRS' }
kind: 'StorageV2'
properties: storageAccountProps
}
Output Types
output endpoints resourceOutput<'Microsoft.Storage/storageAccounts@2024-01-01'>.properties.primaryEndpoints
Do not do this
Do not use a type definition for resource-derived types but define them directly in parameters or outputs.
type managedRulesDefinitionType = resourceInput<'Microsoft.Network/ApplicationGatewayWebApplicationFirewallPolicies@2024-07-01'>.properties.managedRules type customRulesType = resourceInput<'Microsoft.Network/ApplicationGatewayWebApplicationFirewallPolicies@2024-07-01'>.properties.customRules type policySettingsType = resourceInput<'Microsoft.Network/ApplicationGatewayWebApplicationFirewallPolicies@2024-07-01'>.properties.policySettings type wafPolicyTagsType = resourceInput<'Microsoft.Network/ApplicationGatewayWebApplicationFirewallPolicies@2024-07-01'>.tags