Victor walks Cloud Shell: clone, read main.bicep, what-if, deploy, re-run to prove idempotency, then tear down.
Run one step, then check it
Read the step before copying. Bash commands belong in Git Bash on Windows or Azure Cloud Shell set to Bash; PowerShell uses different syntax. A JSON, YAML, Bicep or application-code example belongs in the named file, not directly at the terminal prompt. Follow any shell-specific instructions below.
Keep the same terminal open so earlier variables remain available. Copy one complete command block without the terminal's $ prompt. A backslash continues a command onto the next line. If Bash shows a lone >, it is waiting for the rest of a quote, command or file block. Finish the complete block; if the paste was incomplete, press Ctrl+C and copy it again. Do not paste the next step into an unfinished command.
A line such as RG="training-group" stores a value without printing it. Output captured with $(...) is also stored; a blank screen alone does not prove failure. A sleep deliberately waits, and -o none suppresses normal output. Wait for your normal prompt, then run the step's checkpoint. After an error, stop and keep the exact command and message; later steps cannot repair a missing resource. Never share passwords, access tokens or secret values in a screenshot.
Imperative clicks vs. declarative code
There are two ways to create anything in Azure, and this lab is really about the difference between them. The imperative way is a command that does an action — az storage account create — fine for a one-off, but it leaves no record of intent and can behave differently on a second run. The declarative way is a file that describes the desired end state, and Azure makes reality match it. Re-running is safe and does nothing if reality already matches. That second way is how real infrastructure is managed, and Bicep is Azure's language for it.
By the end of the next twenty minutes you will have deployed a storage account from a template, previewed exactly what a deployment would do before doing it, and seen a re-run correctly report that there is nothing to change. Small resource; the point is the method.
A certificate claims. A template proves.
You need a free Azure account, the Azure CLI (az), then be signed in with az login. First time? The 15-minute Set up your machine page covers the account, the installs (winget / brew / apt), and sign-in. Prefer zero installs? Run everything in Azure Cloud Shell (Bash), preinstalled and already signed in.
Every CAMPUX lab lives in a public repo — the guide, the Bicep template, and any code. This page walks the same steps; the files are at github.com/kloudcaptain/campux-labs. Run everything in Azure Cloud Shell (Bash), where az and bicep are already installed.
Get the template and a place to put it
Coming straight from Class 12? You have not met Git or Bicep yet, and you do not need either to start. Use the first path: it writes the template with the same file block you practised in the terminal warm-up. If you have done Classes 17 and 20, the second path clones the full version from the repository.
Path 1, no Git. Open Cloud Shell (Bash, which already has Bicep), make a folder, and write the file. Paste the whole block, from cat down to the line that says only EOF:
mkdir -p first-bicep && cd first-bicep
cat > main.bicep <<'EOF'
param location string = resourceGroup().location
param sku string = 'Standard_LRS'
resource storage 'Microsoft.Storage/storageAccounts@2023-05-01' = {
name: 'campux${uniqueString(resourceGroup().id)}'
location: location
sku: {
name: sku
}
kind: 'StorageV2'
properties: {
minimumTlsVersion: 'TLS1_2'
allowBlobPublicAccess: false
supportsHttpsTrafficOnly: true
}
}
output storageAccountName string = storage.name
EOF
az bicep build --file main.bicep && echo "compiles"
Read it as three parts, the same three choices you made by hand in Class 12:
paramlines are inputs with defaults.locationborrows the resource group's region;skuis the redundancy dial, LRS to start.- The
resourceblock is the storage account. The text in quotes afterresource storageis its type and API version.uniqueString(...)builds a name no one else has, because storage names are global. The threepropertiesare the secure settings from Class 12: modern TLS, no anonymous blob access, HTTPS only. outputhands one value back after the deploy, here the account's name, so you do not have to look it up.
az bicep build only checks the file and turns it into the JSON Azure reads; it creates nothing. If it prints an error with a line number, compare that line with the block above. If it prints compiles, carry on.
Path 2, after Class 17. The repository version adds descriptions, allowed values, tags and two more outputs. Same steps after this point.
git clone https://github.com/kloudcaptain/campux-labs.git cd campux-labs/lab-first-bicep-storage/bicep cat main.bicep
Now make a resource group to deploy into:
RG="campux-lab-bicep-rg" az group create --name "$RG" --location eastus
"provisioningState": "Succeeded", and your template compiled (path 1) or printed (path 2): parameters, one storage resource, and its outputs. That whole file is your infrastructure.Preview with what-if — look before you leap
what-if shows exactly what a deployment would do without doing it. Professionals run this before every deploy; it is the difference between a change you understood and a change you hoped about.
az deployment group what-if --resource-group "$RG" --template-file main.bicep
+ Microsoft.Storage/storageAccounts/… — a Create. Nothing exists yet; this is only a preview of what applying the template would do.Deploy
az deployment group create --resource-group "$RG" --template-file main.bicep --name firstbicep
Read back what it made, straight from the deployment's own outputs:
Commands inside $(...) save their output in a variable. Check the printed identifier or the checkpoint before continuing.
SA=$(az deployment group show -g "$RG" -n firstbicep --query properties.outputs.storageAccountName.value -o tsv)
az storage account show --name "$SA" --resource-group "$RG" \
--query "{tls:minimumTlsVersion, publicBlob:allowBlobPublicAccess, sku:sku.name}" -o json
TLS1_2, publicBlob: false, and Standard_LRS. The secure settings are written into the template, so no deployment of it can quietly forget them — the reason to template storage instead of clicking it.The payoff: idempotency
Run the exact same preview again, against the resource you just created:
az deployment group what-if --resource-group "$RG" --template-file main.bicep
+ Create and no - Delete — your account is not being recreated. Most runs show the resource marked = with a summary line ending no change. You may instead see a ~ Modify listing only a few managed properties as noise even though nothing meaningful changed — a known quirk, not your template rebuilding anything. Either way, the point stands: the file describes the end state, reality already matches, so a re-run does nothing destructive. Safe to run a hundred times.Change the file, see the diff
Change one input — the redundancy SKU — and preview again. Bicep tells you precisely what would change, before anything happens:
az deployment group what-if --resource-group "$RG" --template-file main.bicep --parameters sku=Standard_GRS
~ Modify with sku.name: "Standard_LRS" => "Standard_GRS". You changed one value and the tool reported the exact effect in advance. Apply it for real with az deployment group create … --parameters sku=Standard_GRS if you like.Tear it down
Always the last step of every lab. Delete the group and confirm it is gone, so nothing lingers to bill you.
az group delete --name "$RG" --yes
az group exists --name "$RG" # -> false
az group exists returns false. Your subscription is back exactly where it started.What you can now honestly claim
You authored and deployed an Azure resource from a Bicep template, previewed a deployment with what-if, saw that a re-run of an unchanged template changes nothing, and read a precise diff before applying a change. That is the on-ramp to everything else in cloud engineering: real infrastructure lives in files like this one, under source control, reviewed before it ships. From here, the same ideas apply to something with access control — the next lab gives an App Service a managed identity and a least-privilege role, with a Bicep path built on exactly what you just learned.
- The three secure properties in the template — TLS 1.2 minimum, no public blob access, HTTPS only — are also current Azure defaults, so they will not by themselves show up as a change. They are written explicitly anyway, because "the default today" and "guaranteed in writing" are not the same promise, and the second is the one an auditor accepts.
- Times and costs are honest estimates. An empty StorageV2 account costs effectively nothing at rest, but the teardown step is not optional — a resource forgotten in a corner is how a free experiment becomes a line item.