Featured image of post Azure Bicep Parameters Explained: A Hands-On Guide

Azure Bicep Parameters Explained: A Hands-On Guide

Learn how to use parameters in Azure Bicep to create flexible, reusable infrastructure templates. This hands-on guide walks you through real-world examples, best practices, and deployment workflows to help you manage Azure environments more effectively.

Introduction

In our previous article, we created and deployed our first Azure Bicep script—a cleaner, more readable alternative to traditional JSON-based ARM templates. You might remember that we hardcoded a value like 'australiaeast' for the location. And if you were paying close attention, you probably noticed a subtle squiggly line in the editor, hinting that there might be a better way.

That better way is what we’re focusing on in this article: parameters.

Parameters are one of Bicep’s most powerful features. They let you design templates that are flexible, reusable, and environment-agnostic. Instead of editing the code every time you want to change a resource name, region, or configuration, you can pass in different values—keeping your deployments clean and consistent across teams, projects, or stages like dev, test, and prod.

In this article, we’ll walk through:

  • What parameters are and why they matter in real-world infrastructure
  • How to define and use them in a Bicep file
  • How to pass values at deployment time using the Azure CLI
  • Best practices for writing clean, adaptable templates

So let’s dive in—starting with what parameters actually are, and why they’re such a game-changer in infrastructure as code.

What Are Parameters in Bicep?

When we first wrote our Bicep script, we hardcoded values like the Azure region ('australiaeast') and the resource group name. That worked fine for a single environment—but what happens when we want to deploy the same infrastructure to a different region, or use a different naming convention in production?

This is where parameters come in.

Let’s start with something important:

Parameters aren’t “built-in” features you just pick from a list. They’re something you define.

In other words, you decide what becomes a parameter. If there’s a value in your template—like a location, a name, a VM size, or a network range—and you think it might change across environments or teams, you can choose to make that a parameter. Just like that, it becomes configurable.

That’s what happened in our first template. We hardcoded the location by writing it directly into the script like this:

1
location: 'australiaeast'

📘 Quick note: “Hardcoded” just means writing a specific value directly into your file. That value never changes unless someone opens the file and edits it manually.

Instead of doing that, we can create a parameter, which lets us inject a value from the outside:

1
param location string = 'australiaeast'

Now we’re saying:

“Hey Bicep, this value might need to change. Let the person deploying the template provide a value—but if they don’t, fall back to this default.”

Think of parameters as customisable knobs you place at the top of your template. You decide which values should be adjustable, and everything else can stay fixed.

This makes your Bicep files:

  • Flexible enough to support different environments
  • Easier to maintain
  • Safer to reuse across teams or projects

And most importantly, it puts you in control of what’s configurable—and what’s not.

What’s Next?

Now that we’ve clarified what parameters are—and more importantly, what they aren’t—let’s explore how to choose which values are worth turning into parameters, and how to strike the right balance between flexibility and simplicity.

Azure Bicep

Choosing What to Parameterise

Just because you can parameterise something in Bicep doesn’t mean you should.

One of the most common mistakes when getting started with infrastructure as code is to either parameterise too much—turning every detail into a configurable input—or too little, locking everything down and losing flexibility. The key is knowing which values are worth exposing as parameters.

Let’s take a moment to think about what parameterisation really means.

When you define a parameter in Bicep, you’re saying:

“This part of the deployment might change between environments, teams, or use cases—so let’s make it configurable.”

A Simple Analogy

Think of it like ordering coffee. You tell the barista your size, milk preference, and flavour—those are your parameters. But you don’t need to specify the water temperature, bean grind, or espresso pressure. Those are standard, consistent, and rarely change.

If you had to configure every detail for every cup, it would be exhausting—and error-prone. Infrastructure is no different.

In the same way, parameters in Bicep act like input knobs at the top of your template. You expose the things that should be tweakable—like size or region—and lock down the rest. This makes your deployments consistent, but still flexible where it counts.

When to Use Parameters

Here are some examples of properties that are usually good candidates for parameterisation:

  • Resource names that change between environments

    • Example: web-app-dev-rg, web-app-test-rg, web-app-prod-rg
  • Azure regions where you deploy infrastructure

    • Primary: australiaeast,
    • Secondary: australiasoutheast
  • Resource SKUs or VM sizes

    • Development: Standard_B1s
    • Production: Standard_D4s_v3
  • Values that change based on team or project context

    • IP address ranges
    • Subnet CIDRs
    • Allowed client IDs or object IDs

These are all values that are likely to differ between environments—or might be managed by different teams in the organisation.

When Not to Use Parameters

Not every value needs to be configurable. Sometimes hardcoding is simpler and more appropriate—especially for values that rarely change.

Avoid parameterising:

  • Resource types (e.g. "Microsoft.Web/sites")
  • API versions
  • Static tags that apply everywhere (e.g. "owner": "platform-team")
  • Internal resource names used only inside the template

Keeping these details fixed makes your templates easier to reason about and reduces unnecessary complexity for whoever is deploying them.

Striking the Right Balance

The goal is not to parameterise everything. It’s to parameterise with purpose—exposing only the inputs that truly need to be flexible.

A good rule of thumb:
If a value changes between environments, teams, or deployments—it probably belongs as a parameter.

Everything else? Hardcode it and move on. Less clutter. Fewer mistakes.

So now that you know what to parameterise and why, let’s get hands-on.

In the next section, we’ll build a real-world Bicep template using parameters. You’ll see exactly how to define them, use them in your resources, and make your templates cleaner and more reusable—step by step.

Adding Parameters to Your Bicep File

Now that you know what parameters are—and how to decide which values are worth exposing—let’s put that knowledge into practice.

In this section, we’ll take the basic resource group deployment from our previous article and upgrade it. We’ll replace hardcoded values with parameters, making the template cleaner, more reusable, and easier to deploy across different environments or teams.

Step 1: Start with a Basic Template

Here’s the starting point—a simple Bicep file that creates a resource group at the subscription level:

1
2
3
4
5
6
targetScope = 'subscription'

resource myResourceGroup 'Microsoft.Resources/resourceGroups@2022-09-01' = {
  name: 'web-app-dev-rg'
  location: 'australiaeast'
}

Right now, the resource group name and the location are hardcoded. That means if you want to deploy this to a different region, or use a different naming convention, you’d have to open the file and edit it manually each time.

Let’s improve that by making those values parameterised.

Step 2: Define Parameters

We’ll start by defining two parameters: one for the resource group name, and one for the location.

1
2
param resourceGroupName string
param location string = 'australiaeast'

Here’s what’s happening:

  • The first line defines a parameter called resourceGroupName, which expects a string value.
    • Since there’s no default value, Bicep will require you to supply one at deployment time.
  • The second line defines a location parameter, also of type string—but this time, with a default value of 'australiaeast'.

That means if you don’t explicitly pass a value for location, it will default to 'australiaeast'. But if you do supply a different one—say 'westus'—Bicep will use that instead.

💡 Remember: By defining a value as a parameter, you’re telling Bicep,
“This might change between deployments—so let’s make it configurable.”

📝 Quick Note on Defaults

Not every parameter needs a default—and in some cases, you might not want one.

  • Use a default if there’s a common value that works most of the time.
  • Omit the default if you want to force the deployer to be explicit.

The key is to strike a balance between convenience and control.

Step 3: Use the Parameters in Your Resource

Now let’s plug those parameters into the resource declaration:

1
2
3
4
resource myResourceGroup 'Microsoft.Resources/resourceGroups@2022-09-01' = {
  name: resourceGroupName
  location: location
}

This might look simple—but it’s a powerful shift.

  • Instead of hardcoding values, we’re now referring to the parameters we defined above.
  • During deployment, Bicep will substitute in whatever values are passed in—either from the command line or a parameter file.

So when Bicep sees name: resourceGroupName, it’s not creating a group called “resourceGroupName.”
It’s using the value of that parameter as the actual name.

Optional Enhancements: Add Descriptions and Allowed Values

To make your parameters more user-friendly and less error-prone, Bicep allows you to add annotations:

1
2
3
4
5
6
7
8
9
@description('The name of the resource group to create.')
param resourceGroupName string

@allowed([
  'australiaeast'
  'australiasoutheast'
])
@description('The Azure region for the resource group.')
param location string = 'australiaeast'

These annotations do a few useful things:

  • @description helps anyone reading or deploying your template understand the purpose of the parameter.
  • @allowed restricts values to a specific list—reducing typos and helping standardise your environments.

These small touches go a long way in making your templates clear, safe, and easy to reuse across teams.

Recap

At this point, you’ve taken a static template and made it dynamic:

Before After
Hardcoded values Configurable parameters
One-off usage Reusable across regions and environments
Risk of manual errors Safer, structured input via annotations

And the best part? Your Bicep file didn’t get more complicated—just more flexible.

What’s Next?

You’ve defined parameters and wired them into your Bicep file. But how do you actually supply values when you deploy?

In the next section, we’ll walk through how to pass parameters at deployment time using the Azure CLI. You’ll see how powerful even a simple template becomes when it’s parameterised.

Deploying a Parameterised Bicep File

With your parameters defined and integrated into the template, it’s time to deploy your Bicep file and pass in real values.

Let’s walk through this step by step using the Azure CLI.

Step 1: Make Sure You’re Signed in to Azure

Before deploying anything, make sure your Azure CLI is authenticated and pointed to the correct subscription:

1
az login

If you have access to multiple subscriptions, use this to set the right one:

1
az account set --subscription "<your-subscription-name-or-id>"

Step 2: Navigate to Your Bicep File

Open a terminal and change to the folder where your main.bicep file is saved:

1
cd path/to/your/bicep/project

Step 3: Run the Deployment Command

Use the following command to deploy the template and pass in parameter values:

1
2
3
4
az deployment sub create \
  --location australiaeast \
  --template-file main.bicep \
  --parameters resourceGroupName='web-app-test-rg'

Let’s break this down:

Command Part What It Does
az deployment sub create Tells Azure to perform a subscription-scope deployment
--location australiaeast Specifies where to store deployment metadata
--template-file main.bicep Points to the Bicep file with your infrastructure definition
--parameters ... Passes in a value for the resourceGroupName parameter

🧠 You might be wondering: Why are we specifying --location australiaeast again, even though we already passed a location parameter inside the template?

That’s a great question—and it’s easy to mix up, so let’s clear it up.

There are two different “locations” at play here, and they serve completely different purposes:

Purpose Type Used For
--location in the CLI command Deployment metadata Tells Azure where to store logs and operation history for this deployment
location parameter in the Bicep file Resource configuration Specifies where to actually create the Azure resources

To make this less abstract, here’s a helpful analogy:

🗂️ Think of the --location in the CLI as choosing which data centre stores the receipt for the deployment operation. It’s where Azure keeps a record of what happened, not where your resources go.

Meanwhile, your location parameter inside the Bicep file controls the real destination—the Azure region where your resource group (or other resources) will actually live.

So yes, in our example, both happen to say australiaeast, but they’re not connected—and they serve different roles.

Step 4: Confirm the Deployment

If everything works, you’ll see a confirmation message that includes a provisioning status:

1
2
3
4
5
6
7
8
{
  "id": "/subscriptions/xxxxxx/resourceGroups/web-app-test-rg",
  "name": "web-app-test-rg",
  "properties": {
    "provisioningState": "Succeeded"
  },
  ...
}

🎉 Nice work! You’ve just deployed a resource group using a parameterised Bicep template. That’s a big step toward writing flexible, reusable infrastructure code.

And the best part? You didn’t have to touch the template at all—just passed in values at deployment time.

💡 Try It Yourself: Deploy to a Different Region

Now that we’ve seen how to deploy using parameters, here’s where the real flexibility kicks in.

Let’s say we want to deploy the exact same resource group—but in a different region, like westus. Thanks to parameterisation, we don’t need to touch the Bicep file. We simply pass different values at deployment time:

Deploying to Australia East

1
2
3
4
az deployment sub create \
  --location australiaeast \
  --template-file main.bicep \
  --parameters resourceGroupName='myResourceGroupAUE' location='australiaeast'

Deploying to West US

1
2
3
4
az deployment sub create \
  --location westus \
  --template-file main.bicep \
  --parameters resourceGroupName='myResourceGroupWUS' location='westus'

In both cases:

  • We’re reusing the exact same Bicep template.
  • We’re overriding the default location by passing a new value for the location parameter.
  • Azure will create the resource group in the specified region, and store the deployment metadata in the same region using the --location flag.

This is one of the big advantages of parameterisation:

You can deploy the same infrastructure across multiple environments, regions, or projects—without ever modifying the source file.

Checking the Results in the Azure Portal

Once you’ve run the deployment commands, let’s confirm everything worked by visiting the Azure portal.

  1. Go to portal.azure.com
  2. In the left-hand menu, select Resource Groups
  3. Search for the two resource group names you used:
    • myResourceGroupAUE for Australia East
    • myResourceGroupWUS for West US

🗺️ Boom—you’ve just deployed the same infrastructure to two completely different regions using one shared template.

This is the heart of what makes Bicep (and infrastructure as code in general) so powerful: one template, many possibilities. You’re no longer duplicating code for each region—you’re just adjusting parameters.

Australia East Resource Group
West US Resource Group

If they show up—congratulations! You’ve now deployed parameterised infrastructure to two different regions using the same Bicep file. That’s a major step toward reusable, scalable infrastructure as code.

What’s Next?

So far, we’ve passed parameter values directly through the command line. That’s great for quick tasks, but it can become hard to manage across multiple environments or teams.

In the next section, we’ll explore how to use a parameter file to streamline deployment—especially in larger projects or CI/CD pipelines.

Using a Parameter File

So far, you’ve passed values into your Bicep template directly through the CLI. That’s great for quick tests or small one-off deployments.

But what happens when your project starts to grow?

  • You’ve got five or six parameters
  • You’re deploying to multiple environments
  • Your command line starts to feel like a scroll of flags and values

That’s where parameter files shine.

🧩 A parameter file is your clean, reusable configuration sheet. It separates environment-specific values—like names, regions, SKUs—from the logic of your infrastructure.

It helps you:

  • Keep your deployments tidy
  • Reuse the same template across multiple environments
  • Avoid mistakes from long, cluttered CLI commands
  • Plug into pipelines and source control cleanly

In short: when your deployments start growing up, so should your parameter strategy.

Let’s take a look at how it works.

Before We Jump In: What Is a Parameter File, Really?

Before we look at an example, let’s clear up what a parameter file does—and doesn’t do.

✅ A parameter file is a JSON file that you create
✅ It provides values for parameters defined in your Bicep file
✅ It keeps environment-specific settings (like names, regions, or SKUs) outside your template
✅ It’s great for managing multiple environments or plugging into CI/CD pipelines

❌ It does not define the parameters themselves
❌ It does not replace the .bicep file
❌ It’s not required for small or quick deployments—but becomes very helpful as things scale

Think of the .bicep file as your blueprint, and the parameter file as the set of instructions for this specific build.

Here’s what a simple parameter file looks like:

📄 bicep.parameters.json

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "resourceGroupName": {
      "value": "myResourceGroupAUE"
    },
    "location": {
      "value": "australiaeast"
    }
  }
}

Each key under parameters must match a parameter defined in your Bicep file. The value assigned here is what gets used during deployment.

So to summarise:

You’re separating infrastructure logic (in your Bicep file) from environment-specific values (in your parameter file).

And while this example is small, in real-world projects your parameter file might define:

  • Resource group names
  • Azure regions
  • VM sizes
  • App settings
  • Tag structures
  • IP ranges
  • Feature toggles
  • …everything that makes an environment unique

That’s why parameter files are especially useful for deploying complete environments.

Why Parameter Files Matter

Once your infrastructure starts growing, parameter files become especially helpful. Here’s why:

  1. Environment separation
    You can create a different file for each environment:

    • dev.parameters.json
    • test.parameters.json
    • prod.parameters.json

    Each file contains different values—but they all use the same .bicep file as their base.

  2. Cleaner CLI commands
    Rather than passing ten parameters in a single command, you just reference one file.

  3. Team collaboration
    Multiple teams can use the same infrastructure template but supply their own parameter values—without stepping on each other’s changes.

  4. Better for CI/CD
    Parameter files are easy to track in source control and plug into pipelines, giving you clarity, consistency, and repeatability.

Using the Parameter File in Deployment

To use a parameter file during deployment, run:

1
2
3
4
az deployment sub create \
  --location australiaeast \
  --template-file main.bicep \
  --parameters @bicep.parameters.json

A few things to note:

  • The @ symbol tells Azure to load parameters from a file
  • The file must match the parameters declared in the Bicep template
  • You still need to pass --location in the CLI to indicate where to store the deployment metadata.

And just like that—you’ve made your deployments cleaner, repeatable, and easier to manage.

No more scrolling through a sea of CLI flags. With a single parameter file, you’ve separated logic from configuration—a best practice used by teams building production-scale infrastructure.

Recap

A parameter file is:

  • Something you create
  • A JSON-based structure for feeding values into your template
  • A powerful tool for managing environments, avoiding duplication, and keeping your CLI and pipeline logic clean

What’s Next?

Well done—you’ve just taken a major step forward in mastering Azure Bicep. You’ve moved from static, hardcoded templates to flexible, reusable infrastructure definitions. That’s a serious upgrade—and a foundational skill in infrastructure as code.

You now know how to:

  • Define parameters to make your templates configurable
  • Choose what to parameterise and why
  • Pass in values from the CLI or a JSON file
  • Write cleaner, safer, and more reusable deployment files

But parameters are just one part of the story.

Sometimes you need intermediate values—things like dynamic names, calculated strings, or conditions that change based on input. That’s where variables come in.

If parameters are the input knobs at the top of your template, variables are the gears turning behind the scenes—letting you build smarter logic, avoid duplication, and keep things tidy.

🧩 In the next article, we’ll dive into Bicep variables—how to define them, when to use them, and how they work together with parameters to keep your infrastructure clean, modular, and expressive.

Stay tuned—it’s going to be a fun one.