Commencing today, we initiate a series of articles delving into the deployment of Azure resources using Bicep. Throughout this exploration, we will elucidate how this emerging practice aligns with the broader paradigm of infrastructure as code.
Introduction
Over the past decade, the observed trend of cloud adoption has prompted numerous organizations to transition a multitude of their applications onto the cloud, encouraging a more profound embrace of this paradigm and this migration necessitated the adoption of more stringent processes for developing and deploying applications.
-
This trend primarily impacted applications, prompting engineers to increasingly adopt CI/CD best practices facilitated by the integrated tools offered by cloud providers, such as Azure DevOps, for instance.
-
A burgeoning trend is currently unfolding wherein automation extends beyond applications to encompass infrastructure. Resources are no longer manually allocated; instead, they are automatically provisioned through configuration files. This paradigm is commonly known as "infrastructure as code".
Various tools have surfaced to cater to the infrastructure-as-code paradigm, with Terraform and Ansible being among the most prominent. Recently, Microsoft has introduced a newcomer called Bicep, and in this series of articles, we will explore its application in real-world scenarios.
The subsequent textbooks prove useful for concluding this series, addressing infrastructure as code as a comprehensive subject matter.
This article was originally posted here: Leveraging Bicep to deploy Azure resources
What is Infrastructure as Code (IaC)?
Infrastructure as code (IaC) is a methodology that involves managing and provisioning computing infrastructure through machine-readable script files, rather than through physical hardware configuration or interactive configuration tools. In essence, it treats infrastructure —such as servers, networks, and databases— as code, allowing it to be versioned, tested, and deployed with the same practices used for application code.
With infrastructure as code, configurations are expressed in a declarative or imperative scripting language, specifying the desired state of the infrastructure. This script, often referred to as a "template" or "manifest", can be used to automate the provisioning and configuration of infrastructure components.
Streamlining the Automation
IaC allows for the automated provisioning and management of infrastructure, reducing manual intervention and the risk of human error.
Ensuring Version Control
Infrastructure configurations can be versioned using version control systems, enabling tracking changes over time, rollback to previous states, and collaboration among team members.
Ensuring Reproductibility
Infrastructure can be consistently reproduced across different environments (development, testing, production) by using the same codebase, minimizing discrepancies and improving reliability.
Why Now?
The challenges mentioned earlier are not novel; they have persisted since the inception of computer science. However, the fundamental shift lies in the historical provisioning of servers on premises, where tools for automating deployments were either unavailable or challenging to implement. With the widespread adoption of cloud computing, cloud platforms have introduced methodologies and mechanisms to streamline these processes. Consequently, applying automation has become imperative, especially in the early stages of a project's lifecycle.
IaC has rapidly evolved from a pioneering concept to a foundational element of the software development process.
Presently, there are numerous providers for IaC, with Terraform and Ansible standing out as the most renowned. Notably, Microsoft has introduced Bicep, a tool specifically designed for the Azure platform, adding to the array of choices available for IaC implementation.
Terraform is actively developed by HashiCorp and enables users to define and provision infrastructure in a declarative configuration language. With Terraform, we describe the components and resources needed for our infrastructure in a high-level configuration file, and then Terraform automates the process of provisioning and managing those resources.
-
Terraform uses a declarative language to define the desired state of infrastructure. Users specify what resources they need and their configurations, and Terraform handles the process of figuring out how to make the current infrastructure match the desired state.
Information
We will observe that Bicep also utilizes a declarative syntax for provisioning infrastructure, where we articulate the intended outcome without specifying the individual steps to achieve that result.
-
Terraform keeps track of the state of the infrastructure in a state file. This file records the relationships between resources and their current configurations, allowing Terraform to make informed decisions during subsequent runs.
Information
The state file, essential for Terraform to monitor changes, needs to be stored in a designated location. This can be accomplished through Azure Storage or Terraform Cloud. Notably, Azure simplifies this process for Bicep by autonomously managing the storage of the state file.
What is Ansible?
Ansible is an open-source automation tool developed by Red Hat. It simplifies complex tasks and facilitates the management of IT infrastructure by allowing users to describe their infrastructure as code.
Ansible serves as a notable illustration of an imperative IaC tool, employing YAML-based playbooks to articulate a series of tasks.
Information
Imperative IaC is an approach where the user specifies the step-by-step sequence of actions that need to be taken to achieve the desired infrastructure state. In imperative IaC, the focus is on detailing the specific tasks and operations that should be executed, often resembling a procedural or script-like style.
While imperative IaC provides a high level of control over the deployment process and greater flexibility, it can sometimes be more complex to maintain and may require more manual intervention to handle changes or updates.
Our objective here is to introduce Bicep, showcase its practical application, and explore how it may present unique capabilities or advantages that could pose challenges to its competitors in the IaC landscape.
How to Deploy Resources on Azure?
Deploying resources on Azure can be done directly through the Azure portal, which provides a graphical user interface (GUI) mode, or in a more technical manner using the Azure CLI. While these methods are convenient, they lack the capability to automate processes or ensure reproducibility. This limitation led Azure to introduce infrastructure as code with ARM templates, allowing for a more systematic and automated approach to resource deployment.
What are ARM Templates?
ARM templates are JSON files that define the infrastructure and configuration for our project. These templates use declarative syntax, which lets us state what we intend to deploy without having to write the sequence of programming commands to create it. Here is an example of an ARM template to create a storage account.
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2022-09-01",
"name": "mystorageaccount",
"location": "East US",
"sku": {
"name": "Standard_LRS",
"tier": "Standard"
},
"kind": "StorageV2",
"properties": {}
}
]
}
This particular template specifies the creation of a storage account named "mystorageaccount
" in the "East US
" region, using the "Standard_LRS
" SKU.
By the way, we observe the essence of declarative; in this context, we articulate what we wish to deploy without specifying the procedural steps. In practice, Microsoft interprets the file in an imperative manner, converting the definition into the corresponding REST API operation. This operation is then transmitted to the Microsoft.Storage
resource provider.
PUT https:
providers/Microsoft.Storage/storageAccounts/mystorageaccount?api-version=2022-09-01
REQUEST BODY
{
"location": "eastus",
"sku": {
"name": "Standard_LRS"
},
"kind": "StorageV2",
"properties": {}
}
Important
This JSON file can subsequently be stored, reused for future purposes, and version-controlled, for example, using Git. It can also be incorporated into a continuous delivery (CD) pipeline within Azure DevOps, enabling the complete automation of the deployment process.
What are the Limitations?
So far, everything aligns with the process. However, the adoption of this syntax by software engineers proved to be challenging; they found it cumbersome and difficult to use. This was especially notable considering that other providers, such as Terraform, offered a much cleaner and intuitive method for declaring resources. In response to this feedback, Microsoft took action by introducing a new, more adapted language. Enter Bicep.
What is Bicep?
Bicep is a declarative syntax for defining ARM templates. It is designed to simplify and enhance the authoring experience for ARM templates.
- Bicep also employs a declarative syntax, allowing users to specify the desired state of their Azure resources without detailing the procedural steps to achieve that state.
- Bicep abstracts the complexity of ARM templates, making it more concise and readable. It provides a higher-level abstraction that is closer to the intent of the user.
- Bicep introduces a type system, enhancing code safety by ensuring that the specified types match the expected Azure resource types.
- Bicep provides built-in validation tools to check for errors and issues in the code before deployment, reducing the likelihood of runtime errors.
Information 1
Remember that Bicep is intended to be a more user-friendly alternative to writing ARM templates directly. It aims to streamline the process of provisioning and managing Azure resources by providing a more intuitive and efficient authoring experience. Indeed, it is not a groundbreaking technology in and of itself, nor is it something unprecedented.
Information 2
Unlike Terraform and Ansible, which are versatile tools usable across various cloud providers such as Azure, AWS, and GCP, Bicep is specifically designed for Azure. It's important to note that Bicep should not be employed if the intention is to provision resources for tools like BigQuery or Redshift.
Now, we will delve into the practical aspects of Bicep. However, before proceeding, we need to install some prerequisites to ensure its proper functionality. Although this may be a bit cumbersome, it is essential for us to move forward and explore concrete examples.
Setting Up Bicep Development and Deployment Environment
Disclaimer
The installation process outlined in this section is somewhat opinionated, emphasizing the use of Visual Studio 2022 for writing Bicep files and deploying them with Azure CLI. However, alternative options exist, such as crafting files in a basic editor and deploying them using PowerShell.
Creating Bicep Files
Mastering Bicep's syntax entirely may not be feasible, but Intellisense comes to the rescue. In this context, we'll utilize the Bicep extension for Visual Studio 2022, which can be downloaded from here. Once the extension is downloaded, simply install it in the IDE.
Deploying Bicep Files
Deploying Bicep files directly from Visual Studio is not currently supported. The deployment of a Bicep file can only be accomplished using Azure CLI or Azure PowerShell, and in this series, we will be opting for Azure CLI.
To install Azure CLI on Windows, follow the instructions provided here.
Disclaimer
For the sake of conciseness, we will omit the creation of management groups here. However, in real-world scenarios, it is advisable to manage them using Bicep for better organization and governance.
Setting Up the Environment
- Open Visual Studio, create a new solution.
We will systematically create the required resources, starting with the subscription.
- To deploy Azure resources, it is imperative to have the necessary permissions, specifically being designated as an Owner at the root scope level. This authorization can be granted using the following Azure CLI command:
az role assignment create --assignee <id_user_assignee> --scope "/" --role "Owner"
"id_user_assignee
" refers to the user account under which Azure CLI commands for deployments will be executed.
It's important to note that the ability to grant access to other users also requires authorization. Being a Global Administrator in Microsoft Enterprise ID alone is not sufficient; explicit declaration is needed to confirm the user's capability to manage access to all Azure subscriptions and management groups in the tenant.
To achieve this, navigate to Microsoft Entra ID, access Properties, and select Yes in the corresponding section.
Creating a New Subscription
Several noteworthy aspects deserve attention at this point.
-
In Bicep, the targetScope
is a property that allows us to specify the deployment scope for the resources defined in a Bicep file. The targetScope
property determines where the resources will be deployed when the template is executed.
-
We have the ability to define certain variables using the param
keyword.
-
The ID of the recently created subscription is accessible through the output variables.
Information 1
The billing scope is essential information for creating a subscription, and obtaining it can be a somewhat intricate process. Follow these steps.
- Execute the command '
az billing account list
' in Azure CLI and note the name property.
- Execute the command '
az billing profile list --account-name "<name_property>" --expand "InvoiceSections"
'
- Note the id property in the
invoiceSections
.
This property is the billing scope.
Information 2
In real-world scenarios involving a CI/CD pipeline, some variables should be configured in the current environment and subsequently replaced at runtime by a dedicated task. As an illustration, the subscription's name might be contingent on the environment (dev, staging, or production) in which it is generated, necessitating a name that appropriately reflects this context.
Important
This code is specifically designed for straightforward Microsoft accounts. However, it needs adaptation, particularly for usage with Enterprise Agreement setups.
Deploying the File
To deploy the file, open Azure CLI and execute the following command:
az deployment tenant create --template-file create.subscription.bicep --location westus
It's evident that the subscription has been successfully created.
Creating a New Resource Group
Having defined a subscription, the next step is to include a resource group within it.
- Create a new file named create.resourcegroup.bicep in the solution.
metadata description = 'Creates a resource group'
targetScope = 'subscription'
param resourceGroupName string = 'Infra'
param location string = deployment().location
resource infraResourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
name: resourceGroupName
location: location
}
-
The targetScope
is now subscription.
-
The resource group's location is acquired through deployment().location
. This variable is actually supplied when the deployment is executed with Azure CLI, requiring us to specify the location explicitly. More on this later.
Deploying the File
Deploying a resource group requires being within a specific subscription. Therefore, it is essential to switch to the designated subscription first, ensuring that resources are provisioned in the correct location.
az account set --subscription EOCS_Bicep
az deployment sub create --template-file create.resourceGroup.bicep --location westus
In the preceding commands, it's evident that we specified the location where the resource group should be deployed. This parameter is subsequently reused by the Bicep file with the deployment().location
helper.
Creating a New Azure Function
Ultimately, we deploy an Azure Function within the resource group. It's important to note that an Azure Function requires an associated hosting plan and a storage account.
Deploying the File
We execute the following command in Azure CLI.
az deployment group create --resource-group Infra --template-file create.azurefunction.bicep
And there we have it: our resources are provisioned, and we can confidently execute these scripts in another data center if the need arises. Furthermore, these Bicep scripts can be version-controlled, allowing for redeployment as needed.
Important
A final note: where is the current deployment's state stored? In other words, how does Bicep determine the existence of resources or those that need creation? In contrast to Terraform, where this state must be explicitly stored, the seamless integration between Azure and Bicep relieves developers of this responsibility (Azure automatically manages this for us).
Final Thoughts
If you wish to delve deeper into this topic, acquire the following books, which encompass all the concepts emphasized in this series and delve into more advanced ones.
Do not hesitate to contact me should you require further information.
History
- 29th January, 2024: Initial version