Hello World in Bicep: IaC made easy

Mateus Lira
5 min readDec 30, 2022

--

If you want to watch it instead of reading it:

If you want to copy instead of watching and reading it:

What should be a Hello World in IaC?

I will create an easy deployment example to get right to the point. Want to know what Bicep is first? Here goes my first article on this theme:

Requirements

  • Azure CLI
  • Bicep install
az bicep install && az bicep upgrade

What are we going to deploy?

We are going to deploy an application. In Azure, we can deploy one application with the App Service. To deploy this app, we have 2 requirements: a storage account and an App Service Plan.

  • App Service
  • App Service Plan
  • Storage Account

Deploying the Storage Account

param location string = 'eastus3'
param storageAccountName string = 'test${uniqueString(resourceGroup().id)}'

@allowed([
'nonprod'
'prod'
])
param environmentType string

var storageAccountSkuName = (environmentType == 'prod') ? 'Standard_GRS' : 'Standard_LRS'

resource storageAccount 'Microsoft.Storage/storageAccounts@2021-08-01' = {
name: storageAccountName
location: location
sku: {
name: storageAccountSkuName
}
kind: 'StorageV2'
properties: {
accessTier: 'Hot'
}
}

Every parameter or variable is used to create our resource, which is the storage account.

  • The first line: a simple parameter choosing the region we are going to use
  • Second line: this parameter uses string interpolation. This is the integration of our string with some strings we don't know yet. Another Concept here is the Object ID retrieving. The “resourceGroup().id” is getting whatever ID our resourceGroup has, and the “uniqueString()” transforms it into a Unique string.
  • @allowed is a Decorator. It is saying: the parameter “environmentType” can only be ‘prod’ or ‘nonprod’
  • storageAccountSkuName is a variable, and if “environmentType” receives the value prod, storageAccountSkuName will have Standard_GRS value, otherwise, Standard_LRS (cheaper).

These creations of variables and parameters are useful because we are creating something that is going to be created again. Plus, we are using it for more than one project. On top of that, we are lazy and we like to copy ourselves.

So, the more we can modularise our code, the best! Speaking of that… I couldn’t stop noticing we have App Service and App Service Plan… This means we have room to create a module!

Creating our first module

module appService 'modules/appService.bicep' = {
name: 'appService'
params: {
location: location
appServiceAppName: appServiceAppName
environmentType: environmentType
}
}

This has the same structure as the creation of the resource. I mean, we are creating a resource here. Am I right?

The first line indicates the kind of deployment we are using.

  • “module” means we have this code locally.
  • “appService” is how we are calling it on the code (we could call it whatever we want to).
  • ‘modules/appService.bicep’ is the location where this function is getting the code from.

Be aware: in Bicep, the “= {“ needs to go on the same line here, because the language understands the end of the line as finishing the command.

param location string
param appServiceAppName string

@allowed([
'nonprod'
'prod'
])
param environmentType string

var appServicePlanName = 'test-launch-plan'
var appServicePlanSkuName = (environmentType == 'prod') ? 'P2v3' : 'F1'

resource appServicePlan 'Microsoft.Web/serverFarms@2021-03-01' = {
name: appServicePlanName
location: location
sku: {
name: appServicePlanSkuName
}
}

resource appServiceApp 'Microsoft.Web/sites@2021-03-01' = {
name: appServiceAppName
location: location
properties: {
serverFarmId: appServicePlan.id
httpsOnly: true
}
}

This is the appService.bicep

I think the previous explanation is enough for you to understand everything here. However, here is my final tip: on the creation of the resource; we are also giving the location the same way we did in the module.

Have a closer look at ‘Microsoft.Web/serverFarms@2021–03–01’. This is the location where we call (by API) the appServicePlan.

We now need outputs!

Yes, how is the main code going to call the appService if this code doesn’t have any output?

output appServiceAppHostName string = appServiceApp.properties.defaultHostName

So, let’s turn possible to get the HostName of the appService! This needs to go on our appService.bicep

Now, we need to call it on our main.bicep:

output appServiceAppHostName string = appService.outputs.appServiceAppHostName

Sounds good, let’s run it!

az deployment group create \
--template-file main.bicep \
--parameters environmentType=nonprod
Portal Azure

So, it worked just fine! Try it for yourself!

Conclusion

We’ve deployed:

  • App Service
  • App Service Plan
  • Storage Account

With code. Just as we intended to. Grand finale for those who stay till the end:

This extension on VSCode is a MUST. Follow me for more content on the cloud, DevOps, SRE, and so on.

--

--

Mateus Lira

Computer Engineer, talking about Micro Services, Cloud Native Solutions and of course, lifestyle - Let's all be healthy mentally and physically.