Badge Claimed: Power of the Shell
For our EcoCraft solution, we are using quite a few different Azure resources…
- A virtual machine, and various associated resources, to host our Minecraft server
- Fabric capacity to host all of our dashboards and reports
- An Azure function app to facilitate connections between Dataverse and our Minecraft server, to issue commands.
- Application Insights to collect data from the various services we are using, such as Copilot Studio agents, Dataverse and more.
Managing all of these complex resources, their dependencies and to ensure we can deploy them cleanly across our development / test / production environments would usually be very challenging. But thanks to Azure Bicep, the whole task becomes ten times easier.
We start first by having our template files setup and prepared within our Azure DevOps Git repository:


This ensures our changes can be tracked effectively, and also provides support for hosting separate parameter files per environment. Next, we can bring in the following YAML deployment pipeline, to handle the deployment to our desired resource group:
trigger: none
parameters:
# List of bicep deployments to run. Add as many items as you want.
- name: bicepDeployments
type: object
default:
- name: 'fabric'
bicepFile: 'EcoCraft.Bicep/EcoCraft.Bicep.Fabric.bicep'
scope: 'resourceGroup' # supported: resourceGroup (below)
subscriptionId: ''
resourceGroup: 'BicepDeployment'
location: 'westeurope'
parametersFile: '' # optional: e.g. infra/network.dev.parameters.json
- name: 'minecraftserver1'
bicepFile: 'EcoCraft.Bicep/EcoCraft.Bicep.MinecraftServer1.bicep'
scope: 'resourceGroup'
subscriptionId: ''
resourceGroup: 'BicepDeployment'
location: 'westeurope'
parametersFile: ''
- name: 'minecraftserver2'
bicepFile: 'EcoCraft.Bicep/EcoCraft.Bicep.MinecraftServer2.bicep'
scope: 'resourceGroup'
subscriptionId: ''
resourceGroup: 'BicepDeployment'
location: 'westeurope'
parametersFile: ''
variables:
# Azure DevOps Service Connection name
azureServiceConnection: 'B&B Subscription'
stages:
- stage: Deploy
displayName: Deploy Bicep
jobs:
- job: DeployBicep
displayName: Deploy all Bicep files
pool:
vmImage: ubuntu-latest
steps:
- checkout: self
- ${{ each d in parameters.bicepDeployments }}:
- task: AzureCLI@2
displayName: "Deploy: ${{ d.name }} (${{ d.bicepFile }})"
inputs:
azureSubscription: $(azureServiceConnection)
scriptType: bash
scriptLocation: inlineScript
inlineScript: |
set -euo pipefail
echo "== Deployment: ${DEPLOYMENT_LABEL} =="
echo "Bicep: ${BICEP_FILE}"
echo "RG: ${RESOURCE_GROUP}"
echo "Location: ${LOCATION}"
if [ -n "${SUBSCRIPTION_ID}" ]; then
echo "Setting subscription: ${SUBSCRIPTION_ID}"
az account set --subscription "${SUBSCRIPTION_ID}"
fi
# Ensure RG exists (optional—remove if RG is managed elsewhere)
az group create --name "${RESOURCE_GROUP}" --location "${LOCATION}" 1>/dev/null
DEPLOYMENT_NAME="${DEPLOYMENT_LABEL}-$(Build.BuildId)"
# Build optional parameter args
PARAM_FILE_ARGS=""
if [ -n "${PARAMETERS_FILE}" ]; then
echo "Using parameters file: ${PARAMETERS_FILE}"
PARAM_FILE_ARGS="--parameters @${PARAMETERS_FILE}"
fi
EXTRA_ARGS=""
if [ -n "${EXTRA_PARAMETERS}" ]; then
echo "Using extra parameters: ${EXTRA_PARAMETERS}"
EXTRA_ARGS="${EXTRA_PARAMETERS}"
fi
az deployment group create \
--name "${DEPLOYMENT_NAME}" \
--resource-group "${RESOURCE_GROUP}" \
--template-file "${BICEP_FILE}" \
${PARAM_FILE_ARGS} \
${EXTRA_ARGS} \
--only-show-errors
env:
DEPLOYMENT_LABEL: ${{ d.name }}
BICEP_FILE: ${{ d.bicepFile }}
RESOURCE_GROUP: ${{ d.resourceGroup }}
LOCATION: ${{ d.location }}
SUBSCRIPTION_ID: ${{ coalesce(d.subscriptionId, '') }}
PARAMETERS_FILE: ${{ coalesce(d.parametersFile, '') }}
EXTRA_PARAMETERS: ${{ coalesce(d.parameters, '') }}
From there, our deployments are completely automated – and green starts to become our favourite colour 🤩

