Bicep User-Defined Functions

A user-defined function packages a repeated calculation under one name. Resource names, tag sets, and formatted text often follow the same pattern many times in one file. A function writes the pattern once, and every use calls it by name.

Picture a coffee machine with a button labeled "latte". Pressing the button runs the same steps every time, and nobody measures milk by hand. A function is that button.

Function Flow

   inputs                 function                  result
 ('shop', 'dev')  ───►  buildName(prefix, env)  ───►  'shop-dev-001'
 ('shop', 'prod') ───►  (same steps each time)  ───►  'shop-prod-001'

Write a Function

The func keyword starts a definition. Each argument needs a type, and the function needs a return type. The arrow separates the header from the result.

func buildName(prefix string, env string) string => '${prefix}-${env}-001'

param environment string = 'dev'

resource storage 'Microsoft.Storage/storageAccounts@2023-01-01' = {
  name: replace(buildName('shop', environment), '-', '')
  location: 'eastus'
  sku: {
    name: 'Standard_LRS'
  }
  kind: 'StorageV2'
}

The function returns shop-dev-001 for the dev environment. The replace call strips hyphens, because storage account names allow only letters and numbers.

Functions That Return Objects

A function builds a full tag set from two inputs.

func buildTags(owner string, env string) object => {
  owner: owner
  environment: env
  managedBy: 'Bicep'
}

var tags = buildTags('platform-team', 'dev')

Rules for Functions

User-defined functions work only with their own arguments. They cannot read parameters or variables declared elsewhere in the file, and they cannot call resource functions such as reference(). Declare each function at the top level of the file, outside any resource.

Share Functions

The @export() decorator publishes a function for other files to import.

// helpers.bicep
@export()
func buildName(prefix string, env string) string => '${prefix}-${env}-001'
// main.bicep
import { buildName } from 'helpers.bicep'

Lambda Functions on Arrays

Bicep includes built-in functions that apply a small inline rule to every array item. The rule uses the arrow symbol and is called a lambda.

map

Transforms every item.

var names = ['web', 'api', 'admin']
var upperNames = map(names, n => toUpper(n))   // ['WEB', 'API', 'ADMIN']

filter

Keeps items that match a test.

var sizes = [1, 5, 10, 20]
var bigSizes = filter(sizes, s => s > 5)   // [10, 20]

sort and reduce

The sort function orders items by a rule, and reduce folds a list into a single value, such as a total.

var total = reduce(sizes, 0, (sum, s) => sum + s)   // 36

Choosing Between a Variable and a Function

A variable fits a value that stays fixed during a deployment. A function fits logic that needs different inputs at different places. Naming rules, tag builders, and address calculators suit functions well.

A Practical Scenario

A team names every resource with the same pattern: application, resource type, environment, and a number. Without a function, each resource repeats the pattern in its own text, and one typo breaks the convention. With a function, the pattern lives in one place. A change to the naming rule takes a single edit, and every resource follows it after the following deployment. Reviewers read less code, since each name shows as a short function call.

Summary

The func keyword defines reusable logic with typed arguments and a return type. Functions see only their own arguments, and @export() shares them between files. Lambda helpers such as map, filter, sort, and reduce process arrays in a single line.