Practice 12: Infrastructure as Code with Terraform
In this practice session, we will learn how to use Terraform to automate the deployment of Azure cloud resources. We will create a Terraform Manifest for deploying our messageboard application in Azure, including all required services (File storage account, CosmosDB, App Service) and service objects (Like databases and containers).
References
- Terraform Azure tutorials: https://developer.hashicorp.com/terraform/tutorials/azure-get-started
- Terraform Azure examples: https://github.com/hashicorp/terraform-provider-azurerm/tree/main/examples
Exercise 12.1 Preparing the environment
In this task, you will set up Azure client libraries and Terraform.
PS! You can use your computer or VM in OpenStack as a development machine. The guide assumes that you use an OpenStack VM.
Setting up VM
- Create a VM in OpenStack VM (Use ubuntu24.04 ).
- Update Linux packages:
sudo apt update
- Choose
Delete Volume on Instance Delete
- Flavour:
g4.r4c2
- Update Linux packages:
Setting up Azure libraries
- Let's install and configure Azure Command Line Interface (CLI) inside the VM:
- Install Azure CLI
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash
- Install Azure CLI
Setting up Terraform
- Follow the official Terraform guide to install it: https://developer.hashicorp.com/terraform/tutorials/azure-get-started/install-cli
- Don't use the Manual installation option. Use the Linux (or Windows) option that uses the default package manager instead.
- Check that Terraform is installed properly:
terraform -help
Configuring Terraform to access Azure API
- We will authenticate to Azure by using the user login command:
- Log in to your Azure account using the command
az login
.
- Log in to your Azure account using the command
- Double-check that Student Subscription is the default active subscription.
- Also, check what is the value of the Student Subscription ID. You will need it later.
az account list -o table
It would be better to set up an Azure 'Service Principal' because this would allow us to automate running Terraform scripts or commands without user intervention. However, it would also require additional permissions, which your accounts do not have in the University Azure tenant.
Exercise 12.2 Creating an Azure Resource Group
We will create our first Terraform Manifest and check how to validate and run it to configure resources inside the Azure cloud.
- Create a directory
terraform-azure
where we will keep our Terraform manifests. - Create a new file named
main.tf
with the content:# Configure the Azure provider terraform { required_providers { azurerm = { source = "hashicorp/azurerm" version = "~> 4.27.0" } } required_version = ">= 1.1.0" } # Define the provider and any extra features provider "azurerm" { features {} subscription_id = var.subscription_id } #Define that an Azure resource group should exist in North EU region resource "azurerm_resource_group" "lab-resource-group" { name = "${var.prefix}-resource-group" location = var.location }
- Create another new file named
variable.tf
with the following content:# We use the prefix to make sure all Azure resources get a unique name variable "prefix" { description = "The prefix used for all resources" default = "pellelab12" } # Location variable controls the Azure region. Change it if you have any issues with creating resources in North EU region variable "location" { description = "The Azure Region in which all resources are created." default = "northeurope" } # The subscription id defines under which subscription the resources will be created variable "subscription_id" { description = "Azure subscription ID" default = "7cab-...-...-643bd" }
- NB! Replace the
prefix
default value with a unique prefix (use your last name, for example: jakovitslab12)! - NB! Replace the
subscription_id
default value with your Student Subscription id!
- NB! Replace the
- Variable file is used to define dynamic values for Terraform templates.
- This makes it easier to reuse the manifest and change the dynamic values without having to modify the Terraform main file.
- If you do not define default variable values, Terraform will ask for the values from you interactively when you apply the manifest.
Validation
- First, let's make sure Terraform install the required plugins, by calling the init command:
terraform init
- Validate that the configuration is proper:
terraform validate
- Apply the Terraform manifest to create the resource group:
terraform apply
- Deliverable: Save the output of the
terraform apply
command (successful run) in a text file named lab12_2.out (or save it as a screenshot named lab12_2.xxx)
- Check that the resource group was created successfully:
- The Name of the created resource group will be
${var.prefix}-resource-group
, where the prefix value is taken from the variables file. - List all resource groups:
az group list
- Check directly:
az group exists -n ${var.prefix}-resource-group
- Replace the ${var.prefix} with the prefix you configured in the variables file.
- The Name of the created resource group will be
Exercise 12.3 Creating a CosmosDB database
Let's now modify the Terraform Manifest to also deploy CosmosDB.
- Add the following block at the end of the Terraform manifest:
resource "azurerm_cosmosdb_account" "lab-cosmosdb" { name = "${var.prefix}-cosmosdb" location = azurerm_resource_group.lab-resource-group.location resource_group_name = azurerm_resource_group.lab-resource-group.name offer_type = "Standard" kind = "GlobalDocumentDB" consistency_policy { consistency_level = "Eventual" } geo_location { location = azurerm_resource_group.lab-resource-group.location failover_priority = 0 } }
- You should note that we can easily refer to the previously created resources by their Terraform names.
- The CosmosDB takes the resource group name from the lab-resource-group resource by using
azurerm_resource_group.lab-resource-group.name
and location or region byazurerm_resource_group.lab-resource-group.location
- where the first element (separated by dots) is the Terraform resource type, the second is the resource name, and the third is the attribute name.
- In a similar fashion, you can later specify CosmosDB attributes like this:
- Cosmosdb account name:
azurerm_cosmosdb_account.lab-cosmosdb.name
- Cosmosdb URL or Endpoint:
azurerm_cosmosdb_account.lab-cosmosdb.endpoint
- CosmosDB primary key:
azurerm_cosmosdb_account.lab-cosmosdb.primary_key
- Cosmosdb account name:
- The CosmosDB takes the resource group name from the lab-resource-group resource by using
Validation
- Validate that the configuration is proper:
terraform validate
- Apply the Terraform manifest to create the CosmosDB:
terraform apply
- Deliverable: Save the output of the
terraform apply
command (successful run) in a text file named lab12_3a.out (or save it as a screenshot named lab12_3a.xxx)
- Check that the CosmosDB was created successfully:
az cosmosdb list | grep lab12
Individual task
- Update the Terraform manifest to also automatically create
sql_database
andsql_container
for storing documents.- Check the documentation for examples: https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/
- For the database, you can use the
azurerm_cosmosdb_sql_database
Terraform resource type- Use the following configuration values:
- Name of the database: "lab5messagesdb"
- Use the following configuration values:
- For the database container, you can use the
azurerm_cosmosdb_sql_container
Terraform resource type.- Use the following configuration values:
- Name of the database container: "lab5messages"
- partition_key_paths: ["/id"]
- partition_key_version: 1
- throughput: 400
- Use the following configuration values:
Validation
- Validate that the configuration is proper:
terraform validate
- Apply the Terraform manifest to create a database and the container:
terraform apply
- Deliverable: Save the output of the
terraform apply
command (successful run) in a text file named lab12_3b.out (or save it as a screenshot named lab12_3b.xxx)
- Check that the CosmosDB database and containers were created successfully and that they have the correct names. Either use the Web interface or use command-line commands.
- Name of the database: "lab5messagesdb"
- Name of the database container: "lab5messages"
- These were the CosmosDB database and container that your App Service should have used in the previous lab if you followed the lab guides directly
Exercise 12.4 Creating a Storage account and container
Let's now modify the Terraform Manifest to also deploy an Azure Storage Account.
- Add the following block at the end of the Terraform manifest:
resource "azurerm_storage_account" "lab-storageaccount" { name = "${var.prefix}storageaccount" resource_group_name = azurerm_resource_group.lab-resource-group.name location = azurerm_resource_group.lab-resource-group.location account_tier = "Standard" account_replication_type = "LRS" }
Validation
- Validate that the configuration is proper:
terraform validate
- Apply the Terraform manifest to create the storage account:
terraform apply
- Deliverable: Save the output of the
terraform apply
command (successful run) in a text file named lab12_4a.out (or save it as a screenshot named lab12_4a.xxx)
- Check that the storage account was created successfully:
az storage account list | grep lab12
Individual task
- Update the Terraform manifest to also automatically create an Azure storage account Container to store images in.
- Check the documentation for examples: https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/
- You should use the
azurerm_storage_container
Terraform resource type and the following configuration values:- Name of the container: "images"
- Container access type: "blob"
- This configures uploaded blobs to be publicly available
Validation
- Validate that the configuration is proper:
terraform validate
- Apply the Terraform manifest to create the storage account:
terraform apply
- Deliverable: Save the output of the
terraform apply
command (successful run) in a text file named lab12_4b.out (or save it as a screenshot named lab12_4b.xxx)
- Check that the storage account container was created successfully and that it has the correct name. Either use the Web interface or use command-line commands.
- Storage container name: images
- This is the Storage account container name that your App Service should have used in the previous lab if you followed the lab guides directly
Exercise 12.5 Creating an App Service
Let's now also deploy an Azure App Service. This step will complete our Terraform manifest to automate the full Azure deployment of the Messageboard application you created in the Lab 5 - Cloud Databases.
- First, let's add the creation of an App Service plan
- NB! It is likely that you can only have a single App Service plan per Azure region.
- If you have not yet deleted the App service plan in the
North EU
region, you may have to delete it before continuing with this task. Alternatively, you could try reusing it by specifying its name in the following Manifest
- Add the following block at the end of the Terraform manifest:
resource "azurerm_service_plan" "lab-app-serviceplan" { name = "${var.prefix}-app-zip-python" location = azurerm_resource_group.lab-resource-group.location resource_group_name = azurerm_resource_group.lab-resource-group.name os_type = "Linux" sku_name = "F1" }
sku_name = "F1"
defines that we want to use Free tier resources.os_type = "Linux"
defines that we want to use a Linux environment.- In some previous labs, students have had issues that Linux environments are not available on some days in some regions. In such cases, it may be better to try different regions.
- Secondly, let's add the creation of an App Service itself
- Add the following block at the end of the Terraform manifest:
resource "azurerm_linux_web_app" "lab-app-service" { name = "${var.prefix}-zipdeploy" location = azurerm_resource_group.lab-resource-group.location resource_group_name = azurerm_resource_group.lab-resource-group.name service_plan_id = azurerm_service_plan.lab-app-serviceplan.id app_settings = { SCM_DO_BUILD_DURING_DEPLOYMENT = "true" STORAGE_ACCOUNT = azurerm_storage_account.lab-storageaccount.name COSMOS_URL = azurerm_cosmosdb_account.lab-cosmosdb.endpoint MasterKey = azurerm_cosmosdb_account.lab-cosmosdb.primary_key CONN_KEY = azurerm_storage_account.lab-storageaccount.primary_access_key } site_config { always_on = "false" application_stack { python_version = "3.9" } } zip_deploy_file = "./lab-app/python-lab-app.zip" }
always_on = "false"
under the site_config specifies that we want to deploy a temporary environment. This is required when deploying App Service under the Free tier.python_version
specifies which Python runtime is usedSCM_DO_BUILD_DURING_DEPLOYMENT
specifies that the Python application should be built while deploying. This is required to install additional libraries using therequirements.txt
file.- We also set up all the necessary Application Settings variables (STORAGE_ACCOUNT, COSMOS_URL, MasterKey, and CONN_KEY) that our Message board application requires.
- It is nice that we can easily fetch the required values from the previously created
lab-storageaccount
andlab-cosmosdb
Terraform resources.
- It is nice that we can easily fetch the required values from the previously created
- As the last step, prepare the Application Service code:
zip_deploy_file = "./lab-app/python-lab-app.zip"
specifies where the code is taken from. We will use a zip file.- Create a
lab-app
subfolder inside your terraform folder. - Take the message board code from Lab 5 - Cloud Databases, where we modified it to use CosmosDB and Azure File storage accounts
- Zip it into a container named
python-lab-app.zip
- NB! make sure zip container uses a flat structure.
- It should not contain another subfolder inside which are your main files.
- Your Python app and requirement files should be in the first level inside the Zip file. Otherwise, Azure deployment code might not find them properly.
- The structure inside the zip file should look something like this:
- Also, make sure the zip file includes everything necessary to run your application:
- Python app file
- requirements.txt file
- Folder where you keep temporary image files
- templates folder with HTML templates
- NB! make sure zip container uses a flat structure.
- Move the
python-lab-app.zip
file into the createdlab-app
subfolder.
Validation
- Validate that the configuration is proper:
terraform validate
- Apply the Terraform manifest to create the Azure App Service:
terraform apply
- Deliverable: Save the output of the
terraform apply
command (successful run) in a text file named lab12_5a.out (or save it as a screenshot named lab12_5a.xxx)
- Check that the App Service was created successfully.
- Check that the Message Board Application is working properly
- Deliverable: Save the screenshot of the working application and name it lab12_5b.xxx (e.g., lab12_5b.png)
- Make sure the URL of the page is visible from the browser
- Make sure at least one message and one image have been entered and are visible on the web page.
- Deliverable: Save the screenshot of the working application and name it lab12_5b.xxx (e.g., lab12_5b.png)
Deliverables:
- Terraform template created by the end of the practice session.
- Output files or Screenshots from the tasks
- lab12_2
- lab12_3a & lab12_3b
- lab12_4a & lab12_4b
- lab12_5a & lab12_5b