Skip to main content

Deploying Azure Function App with Terraform and BitBucket Cloud

This blog post might be outdated!
This blog post was published more than one year ago and might be outdated!
· 5 min read
Florian Horn

The scenario describes one or more developers committing their changes of code for a serverless Azure Function App ("Function-as-a-Service") and managing the required Azure environment resources only by a declaration in Terraform scheme in the code repository to remain the Single Point of Truth (SPOT).

We want to use the events on the repository connected with the specified Terraform Cloud Workspace, e.g. the commit push or the successful merge of a Pull Request to a specific branch, to trigger specific updates in certain environments. Typically, there are some development or quality-check environments, and of course the production.

In the scenario, Terraform will listen to the master branch of the repository, and uses the declared configuration of the environment to set up Azure Cloud with the required resources, which will already be prepared to be connected to the repository to listen to, as to keep the application up to date if further code changes appear.

Preparations

  1. Create a Terraform cloud account
  2. Download Terraform CLI for testing purposes
  3. Prepare a new BitBucket Cloud repository
  4. Connect Terraform and BitBucket
  5. Prepare Azure Subscription
  6. Create a function app

Create a new Terraform Cloud workspace and connect it to your BitBucket Cloud repository.

Terraform Cloud Workspace

Add the required environmental variables to deploy to the Azure Cloud Platform.

Required environment variables to allow access to the Azure Cloud Platform

Terraform Configuration

The following will be specified as a Terraform configuration file (v0.12), e.g. terraform.tf, in the root directory of the project. Multiple files and the module scheme usage are possible, but for simplicity, only one file is used in this scenario. Replace placeholder values with the specific ones of your own setup.

variable "prefix" {
description = "The Prefix used for all resources in this example"
default = "AllInData"
}

variable "location" {
description = "The Azure Region in which all resources in this example should be created."
default = "westeurope"
}

provider "azurerm" {
version = "~>2.0"
features {}
}

resource "azurerm_resource_group" "terraformDemo-rg" {
name = "${var.prefix}_Terraform_Demo"
location = var.location
}

resource "azurerm_storage_account" "terraformDemo-storage" {
name = "allindatatfdemostorage"
resource_group_name = azurerm_resource_group.terraformDemo-rg.name
location = azurerm_resource_group.terraformDemo-rg.location
account_tier = "Standard"
account_replication_type = "LRS"
}

resource "azurerm_app_service_plan" "terraformDemo-appplan" {
name = "${var.prefix}-slotAppServicePlan"
location = azurerm_resource_group.terraformDemo-rg.location
resource_group_name = azurerm_resource_group.terraformDemo-rg.name
sku {
tier = "Standard"
size = "S1"
}
}

resource "azurerm_function_app" "terraformDemo-funcapp" {
name = "${var.prefix}-funcapp"
location = azurerm_resource_group.terraformDemo-rg.location
resource_group_name = azurerm_resource_group.terraformDemo-rg.name
app_service_plan_id = azurerm_app_service_plan.terraformDemo-appplan.id
storage_connection_string = azurerm_storage_account.terraformDemo-storage.primary_connection_string
app_settings = {
"APPINSIGHTS_INSTRUMENTATIONKEY" = "XXXXXXXXX",
"FUNCTIONS_WORKER_RUNTIME" = "dotnet",
"FUNCTIONS_EXTENSION_VERSION" = "~2" ,
"WEBSITE_RUN_FROM_PACKAGE" = ""
}

version = "~2"
https_only = true

site_config {
always_on = true
ftps_state = "Disabled"
http2_enabled = true
}

provisioner "local-exec" {
command = "az functionapp deployment source config --ids ${azurerm_function_app.terraformDemo-funcapp.id} --repo-url https://bitbucket.org/XXX/YYY --branch master --manual-integration"
}
}

Test configuration locally

After installing the Terraform CLI locally on your machine, it is possible to test the configuration without the need of triggering events in the git repository. Currently, the environmental state is persisted on the machine executing the commands. Note, that there will be a fuzzified state mix-up if the state is not persisted remotely in a central storage and the commands are triggered from a different machine.

Terraform Cloud does not know what your local machine knows about the Azure Cloud environment, but there is a solution for this, which I added as a notification at the end of this article.

For testing your Terraform configuration, run the plan command. This just checks with the target environment, but no changes will be applied, so it is saved to execute the command.

terraform plan

For the execution of the Terraform configuration plan (pre-saved or newly processed), use the apply command. It will ask you if you are sure to execute, and if you are, you enter yes to the command line. This is for the local machine. On Terraform Cloud, you can configure in your workspace settings, if an intermitting manual confirmation is required, so both options of supervised and unsupervised execution are possible.

terraform apply

Run remotely on Terraform Cloud

After the correct configuration of our workspace in Terraform Cloud, we add a connection to a BitBucket Cloud repository and add the required Environment variables for Azure Deployment.

If you push code changes to the BitBucket Cloud repository, the Terraform Cloud workspace will recognize those changes nearly immediately and starts the execution of the configuration.

Terraform Cloud workspace plan overview

Review your workspace, if the Terraform plan has been successfully placed, and confirm the execution.

If the Terraform Cloud workspace plan has been placed successfully, confirm to apply

You can now revise the Azure Cloud Platform, and see (maybe with some minor delay) the creation and build-up of the declared environment by Terraform.

Review the environment in Azure Cloud applied by Terraform Cloud

Remote state provider

If you mix the deployment with Terraform Cloud and the local command line, the persisted states get mixed up. The states are stored on the running machine (Terraform Cloud or your local machine), so what you really want in this case is a remote storage for the states. So while we are using Azure Cloud Platform in this scenario, we can store the states there, in a separate and remote Storage Account.