Experiment, Prototype, and Validate Azure Bicep with the Bicep Console

The Bicep console is a feature that helps with prototyping, experimenting, and validating Azure Bicep code snippets. Additionally, you can leverage the Bicep console to validate user-defined functions that are generated with GitHub Copilot to check if the output is as expected.

In this blog, you will learn what the Bicep console is, explore a few use cases, and learn how to use it with GitHub Copilot.

Note! This is currently an experimental feature. Expect limitations and breaking changes.

Blog update 09-03-2026: The section “GitHub Copilot and Bicep console” has been updated. I removed the workaround to validate new functions on the Bicep console and added the console piped input feature instead. This is a feature I requested, which has been implemented in 0.40.2.

Bicep console on the terminal

The Bicep console is an experimental feature that allows you to run Bicep expressions and snippets directly in your terminal. The command to open the console is part of the bicep executable, and you can launch it by running bicep console on the terminal.

The Bicep console uses a REPL (Read–Eval–Print Loop), meaning you get an interactive shell that reads your input, parses it, and then prints the result. This is very handy, as it provides instant feedback within the terminal and does not require an Azure connection to produce output.

The following is supported:

  • Creating user-defined functions using the func keyword
  • Declaring variables using the var keyword
  • Creating user-defined types using the type keyword
  • Direct expressions (e.g. 1 + 1, contains(‘John Doe’, ‘John’), etc.)
  • Use of built-in functions

The Bicep console might look like a small feature, but it’s actually great for developer friendliness, as it removes the hassle of quickly experimenting with Bicep.

Before the console feature, if you wanted to experiment, you had to:

  1. Write a separate .bicep template with outputs and mock data just to test an expression.
  2. Have an Azure connection to start a deployment.
  3. Wait for the output to see whether the expression behaved as expected.

Now, with the Bicep console, you only need to start bicep console and run your expression.

There are a few scenarios where the bicep console can be particularly helpful:

  • Validate complex expressions
  • Learn about the examples from Microsoft docs
  • Validate user-defined functions

In this case, the goal is to validate a complex expression. In the example below, the expression uses multiple lambda functions for data manipulation and combines two arrays.

The purpose of the example below is to produce an output that fits the role assignment format used by the Azure Verified Module (sub-vending module). First, the array varMockedEntraGroupIds (containing mock data) is declared in the console and represents the data used in a parameter of a Bicep template. The variable outRoleAssignments is the output of the Bicep template. This is where the lambda expressions are applied to create the exact role assignment pattern the AVM module expects. Assigning outRoleAssignments to a variable is optional, you can also use the right-hand value directly.

var varMockedEntraGroupIds = [
{
uniqueName: 'Reader-Group'
roleToAssign: 'Reader'
groupId: '11111111-1111-1111-1111-111111111111'
}
{
uniqueName: 'Contributor-Group'
roleToAssign: 'Contributor'
groupId: '22222222-2222-2222-2222-222222222222'
}
{
uniqueName: 'DevOps-Group'
groupId: '33333333-3333-3333-3333-333333333333'
roleToAssign: null
}
]
var outRoleAssignments object[] = union(map(
filter(varMockedEntraGroupIds, item => !contains(item.uniqueName, 'DevOps')),
group => {
principalId: group.groupId
definition: group.roleToAssign
relativeScope: ''
principalType: 'Group'
}
),[
{
principalId: '44444444-4444-4444-4444-444444444444'
definition: 'Reader'
relativeScope: ''
principalType: 'ServicePrincipal'
}
])

This snippet can easily be validated in the bicep console, giving you quick output and instant feedback on what you may need to adjust. In this case, the output should be an array of 3 objects. Below you see the output:

Complex expression output on the bicep console

I like to learn by doing, and learning about the newest functions from Azure Bicep is easier than ever with bicep console. The Microsoft Azure Bicep documentation is comprehensive and includes plenty of Bicep code snippet examples. These examples can be run directly in the bicep console to understand how they work in a practical, interactive way.

Let’s take a look at the function shallowMerge. This function is fairly complex and one you might want to experiment with to understand it in practice. Below is the Bicep code snippet from the Microsoft Docs:

var firstArray = [{ one: 'a' }, { two: 'b' }, { two: 'c'}]
var secondArray = [{ one: 'a', nested: {a: 1, nested: {c: 3}} }, { two: 'b', nested: {b: 2}}]
output firstOutput object = shallowMerge(firstArray)
output secondOutput object = shallowMerge(secondArray)

The bicep console does not support the output keyword, so these should be changed to variables (var), or you can directly run shallowMerge(<input>). Below you see the output:

Microsoft learn example on the bicep console

The best scenario is to validate user-defined functions in the console. It’s a quick way to check whether the function does what you want, or to use it for developing a function.

In the example below, you can see a user-defined function that reverses a string:

func reverse(input string) string => join(map(range(0, length(input)), i => substring(input, length(input) - 1 - i, 1)), '')
view raw udf.bicep hosted with ❤ by GitHub

After you have loaded the user-defined function in the console, you can use the function directly. Below, you can see the output for the values ‘John’ and ‘racecar’:

Validating a custom user-defined function on the bicep console

Can you use the Bicep console to validate the generated code in the console? Yes, it’s possible by using console piped inputs. Piped inputs is a feature I requested (https://github.com/Azure/bicep/issues/18410) to make it easy to pipe functions to the console. This is a new feature released in version 0.40.2. Before this you had to use a workaround using commands like expect, spawn, and send. This is because agents do not work directly on an interactive console, because there is no direct terminal feedback returned to the agent.

---
description: "This chat mode helps you write User-Defined Functions for Azure Bicep"
tools:
[
"runCommands/runInTerminal",
"runTasks",
"edit",
"search",
"think",
"fetch",
"todos",
"runCommands/getTerminalOutput",
]
---
# Azure Bicep User-Defined Function (UDF) Helper
Act as an expert on Azure Bicep User-Defined Functions (UDFs). Your role involves creating and testing these functions to ensure they meet specific requirements and enhance the functionality of Bicep deployments.
## UDF context
Before creating UDFs, use #fetch to review the official documentation:
- <https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/user-defined-functions>
## Workflow for creating UDFs
1. **Understand Requirements**
- Parse user's request for function purpose
- Identify input parameters and expected output
- Breakdown the implementation steps. Use #todos to manage tasks.
2. **Write the UDF**
- Define function signature with parameters and output
- Implement logic using Bicep syntax
3. **Validate Using Bicep Console**
- Use stdin piped inputs to test the function in `bicep console`
- Verify actual output matches expected output
4. **Iterate as Needed**
- Refine function based on test results
## Testing and validation
To test the user-defined functions you use the `bicep console` command which opens a REPL (Read-Eval-Print Loop) environment for Bicep. Do not use the interactive mode of `bicep console` for testing. Instead, use stdin piped inputs to run the functions and verify their outputs`
To test the generated functions, use piped inputs. Use #runCommands/runInTerminal tool to execute commands in the terminal and run the User-Defined Functions on the `bicep console`. There are two versions: one for PowerShell and one for when running in Bash. Use the appropriate one based on the terminal environment::
PowerShell:
```powershell
@'
func myFunction(input string) string => ...
myFunction('test1')
myFunction('test2')
'@ | bicep console
```
Bash using a heredoc:
```bash
cat <<'EOF' | bicep console
func myFunction(input string) string => ...
myFunction('test1')
myFunction('test2')
EOF
```
**Note**: Quote `'EOF'` to prevent `$` expansion (same principle as PowerShell).
# Bicep console limitations
- Limited to expression evaluation and variable declarations
- No support for for-loop expressions, e.g. `[for i in range(0, x): i]` use `map()` instead
# Do the following
- Your only objective is to create and test User-Defined Functions (UDFs) for Azure Bicep.
## Do not do the following
- Do not use `bicep build` to compile Bicep files.
- Do not create or update any other files than `.bicep` files.
- Do not create temporary expect script files.
# Bicep spec - User Defined Functions reverse()
## Function Purpose
This function should reverse a string.
## Function Name
`reverse()`
## Function Input
* Parameter name: input
* Type: string
## Function Output
* Type: string
## Example Function Calling and Output
```bicep
output outReversedName string = reverse('John')
output outReversedRacecar string = reverse('racecar')
```
## Test cases
// Test Case 1
Input: reverse('John')
Expected: 'nhoJ'
Actual: 'nhoJ'
// Test Case 2
Input: reverse('racecar')
Expected: 'racecar'
Actual: 'racecar'
---
description: "Create and test an Azure Bicep User-Defined Function"
agent: Azure Bicep UDF Helper
tools: ["execute/runInTerminal", "edit", "web/fetch", "todo"]
---
# Create Azure Bicep User-Defined Function
Create a Bicep User-Defined Function (UDF) that meets the following requirements and validate it works correctly using the `bicep console`.
## Requirements
- **Purpose**: ${input:purpose:What should this function do?}
- **Input Parameters**: ${input:params:What parameters does it need? (e.g., string value, int length)}
- **Expected Output**: ${input:output:What should it return?}
Test the function thoroughly and confirm it produces the expected results.

In the screenshots below you see:

  1. A prompt is given using /create-bicep-udf and the specification is given to GitHub Copilot.
  2. GitHub Copilot has generated the function based on the specification file.
  3. GitHub Copilot spawns the bicep console, pastes the function and follows the test case as defined in the specification file.
A prompt (1), output from the prompt (2) and validation on the bicep console (3)

The Bicep console may still be experimental, but it already deliver value when working with Azure Bicep. Whether you are validating complex expressions, experimenting with new features, or iterating on user-defined functions, the console gives instant feedback without spinning up a full deployment. It becomes even more powerful with combining it using GitHub Copilot, where you let an agent generate user-defined functions and validate it directly on the bicep console.

2 thoughts on “Experiment, Prototype, and Validate Azure Bicep with the Bicep Console

Leave a comment