fbpx

One of the most popular cloud-native, PaaS (Platform as a Service) products in Microsoft Azure is Azure App Service. It enables you to easily deploy and host web and API applications in Azure. The service supports ways to configure App Settings and Connection String within the Azure App Service instance. Depending on who has access to the App Service instance, you might not want them to be able to see the values for the App Settings or Connection Strings. For this reason, Azure App Service supports integration with Azure Key Vault, a centralized and secure secrets store within Azure. This way, the application can access the secret values at runtime, but anyone with access to manage the Azure App Service instance hosting the application will not be able to see those values as they are stored in Azure Key Vault.

This article shows the steps to deploy and manage both Azure App Service and Azure Key Vault instances using HashiCorp Terraform. Then, it also shows the Terraform configuration code to both set Key Vault secrets from Terraform, as well as pass them security via input variables on the Terraform project from the DevOps deployment pipeline that orchestrates the Terraform commands.

Prerequisites

Before we begin looking at how deploy and configure the Azure App Service and Azure Key Vault instances, you’ll need to make sure you have the following:

  • Azure Subscription – Of course, in order to provision any Microsoft Azure resources, you’ll need an Azure Subscription.
  • Terraform – To run the Terraform commands locally, you’ll need to have Terrafrom installed on your local machine.
  • Azure CLI – To run the Terraform commands locally, you’ll need to have the Azure CLI installed on your local machine.

If you are setting up the Terraform workflow for managing these Azure resources within Azure DevOps, or GitHub Actions, then you don’t need Terraform or Azure CLI installed on your local machine. This is because the necessary commands to execute the workflow will be orchestrated by those DevOps pipelines that you setup.

Need help setting up Azure DevOps Pipelines or GitHub Actions to deploy Terraform resources? If you need help with running Terraform using Azure DevOps or GitHub Actions, please read the following articles:

Step 1: Create Azure Key Vault using Terraform

To start writing the Terraform project for this article, use the following code to provision an Azure Key Vault using Terraform. This code also includes the necessary Terraform for creating the needed Azure Resource Group and configuring the Terraform Azure (azurerm) Provider.

# Declare Terraform Provider
terraform {
  required_providers {
    azurerm = {
      source = "hashicorp/azurerm"
      version = "=3.0.0"
    }
  }
}

provider "azurerm" {
  features {}
}

# Declare Resource Group
resource azurerm_resource_group "primary" {
  name     = "b59-webapp-rg"
  location = "East US"
}

# Declare Azure Key Vault
resource azurerm_key_vault "primary" {
  name                        = "b59-webapp-key-vault"
  resource_group_name         = azurerm_resource_group.primary.name
  location                    = azurerm_resource_group.primary.location
  enabled_for_disk_encryption = true
  enabled_for_deployment      = true
  enabled_for_template_deployment = true
}

Need help getting started with Terraform? If you’re new to Terraform, or just want to get to know it better, then please go read the “Get Started with Terraform” article written by Chris Pietschmann.

Step 2: Store Connection String in Azure Key Vault Secret

Once the Azure Key Vault is created, then the Secrets can be added to it. This can be done from Terraform, and Terraform can even set the secret values too. However, you’ll want to make sure to do this securely, and pass the secret values using Terraform Input Variables so your DevOps pipeline can pass the value in securely.

The following Terraform code can be used to add an input variable to the Terraform project, as well as deploy / manage an Azure Key Vault Secret named DatabaseConnectionString:

# Define Input Variable to Terraform project
variable "database_connection_string" {
  description = "The database connection string to be stored in Key Vault."
  type        = string
}

# Declare Azure Key Vault Secret
resource azurerm_key_vault_secret "databaseconnectionstring" {
  name         = "DatabaseConnectionString"
  # instead of hard coding, populate value from input variable
  value        = var.database_connection_string
  key_vault_id = azurerm_key_vault.primary.id
}

When running the terraform apply command, the database_connection_string input variable will need to have a value specified. This can be done from the command-line as follows:

terraform apply -var="database_connection_string={your-connection-string}"

Keep in mind the {your-connection-string} is a placeholder for the actual value of the Connection String passed in. And, you’ll want to orchestrate this securely from your pipelines by using the variables feature supported by Azure DevOps, GitHub Actions, Terraform Cloud, or any other pipeline orchestrator you’re using.

How are Terraform Input Variables defined and used? If you need help using Terraform input variables, or parameterizing your Terraform projects in this way, them please go read the “Use Terraform Input Variables to Parameterize Infrastructure Deployments” article written by Chris Pietschmann.

Optionally, if you don’t want to set the Azure Key Vault Secret using Terraform, you can use the following Azure CLI command to set the secret from the command-line instead:

az keyvault secret set --vault-name "b50-webapp-key-vault" --name "DatabaseConnectionString" --value "{your-connection-string}"

Or, you could always go into the Azure Portal directly and set the Azure Key Vault secret values there too.

Step 3: Create Azure App Service using Terraform

Azure App Service is the resource that will host the application. It’s also the resource that will have the Connection String and other App Setting configured. To deploy an Azure App Service instance, there are two resources to provision: App Service and App Service Plan.

The App Service is the PaaS service that hosts the application, but the App Service Plan provides the underlying VM infrastructure that does the hosting. So, both these resources are necessary to host an app in Azure App Service.

The following Terrraform code can be added to the project to deploy both an Azure App Service Plan and App Service instance:

# Define Azure App Service Plan
resource "azurerm_service_plan" "example" {
  name                = "b59-webapp-plan"
  resource_group_name = azurerm_resource_group.primary.name
  location            = azurerm_resource_group.primary.location
  sku_name            = "P1v2"
  os_type             = "Windows"
}

# Define Azure App Service instance
# FYI, in Terraform it's called a "Web App"
resource azurerm_windows_web_app "primary" {
  name                = "b59-webapp"
  resource_group_name = azurerm_resource_group.primary.name
  location            = azurerm_service_plan.primary.location
  service_plan_id     = azurerm_service_plan.primary.id

  site_config {}

  app_settings {
    "CustomSetting" = "value"
  }

  connection_string {
    name = "DatabaseConnectionString"
    type = "SQLAzure"
    value = "{your-connection-string}"
  }
}

This example is provisioning a Windows App Service Plan. This means only applications that can be hosted on Windows can be deployed to the Azure App Service / Web App. Alternatively, the azurerm_linux_web_app resource, and os_type = "Linux" configurations could be used to provision a Linux hosted Azure App Service / Web App.

NOTE: In previous versions of Terraform using the AzureRM provider, the azurerm_app_service resource was supported for managing Azure App Service instances. This resource was deprecated in v3.0 of the AzureRM provider, and will be removed in v4.0. As a result, this article shows the use of azurerm_service_plan and azurerm_windows_web_app instead.

The app_settings block within the Web App (azurerm_windows_web_app) resource is used to define Terraform key value map of the App Settings for the App Service instance that’s deployed. In the above example, the App Settings named CustomSetting is getting created with a hard coded value so far.

app_settings {
  "CustomSetting" = "value"
}

The connection_string block within the Web App (azurerm_windows_web_app) resource is used to define the Connection String named DatabaseConnectionString for the App Service instance with a hard coded value so far.

connection_string {
  name = "DatabaseConnectionString"
  type = "SQLAzure"
  value = "{your-connection-string}"
}

Connection String Type Options: The connection_string.type value is used to define what type of Connection String is stored in it’s value. This is what type of service the connection string used to connect to. The possible values for the type are: APIHub, Custom, DocDb, EventHub, MySQL, NotificationHub, PostgreSQL, RedisCache, ServiceBus, SQLAzure, and SQLServer.

Step 4: Configure Connection String and App Setting to Populate from Azure Key Vault Secrets

Now that the Azure Key Vault, Secrets, and Azure App Service are declared to be managed by the Terraform project, the code can be modified to update the Connection String and App Setting on the Azure App Service to populate from Azure Key Vault Secrets instead of being hard coded.

There’s a special formatted string supported by Azure App Service when setting the values of both Connection Strings and App Settings to integrate with Azure Key Vault Secrets. The following is the format for this special Key Vault Secret value format:

@Microsoft.KeyVault(VaultName={vault};SecretName={secret})

The @Microsoft.KeyVault beginning of the value is what Azure App Service looks for to know the value for the Connection String or App Setting will be pull from Key Vault.

The {vault} placeholder is the name of the Azure Key Vault.

The {secret} placeholder is the name of the Azure Key Vault Secret.

There are a couple other format options available in the documentation, but this one is the easiest to use when integrating Azure App Service with Azure Key Vault from within Terraform.

With the Azure Key Vault Secret managed using Terraform, a Terraform expression can be used in place of the {uri} placeholder to have Terraform populate the correct URI for the Azure Key Vault Secret within the expression.

The App Setting or Connection String value using the @Microsoft.KeyVault() formatting, with the Azure Key Vault Name and Azure Key Vault Secret Name populated using Terraform expressions is defined as follows:

@Microsoft.KeyVault(VaultName=${azurerm_key_vault.primary.name};SecretName${azurerm_key_vault_secret.databaseconnectionstring.name})

The following is the updated Terraform code for the Azure App Service / Web App instance with the necessary expression in place for the Connection String and App Setting values to pull from the Azure Key Vault Secret:

resource azurerm_windows_web_app "primary" {
  name                = "b59-webapp"
  resource_group_name = azurerm_resource_group.primary.name
  location            = azurerm_service_plan.primary.location
  service_plan_id     = azurerm_service_plan.primary.id

  site_config {}

  app_settings {
    "CustomSetting" = "@Microsoft.KeyVault(VaultName=${azurerm_key_vault.primary.name};SecretName${azurerm_key_vault_secret.databaseconnectionstring.name})"
  }

  connection_string {
    name = "DatabaseConnectionString"
    type = "SQLAzure"
    value = "@Microsoft.KeyVault(VaultName=${azurerm_key_vault.primary.name};SecretName${azurerm_key_vault_secret.databaseconnectionstring.name})"
  }
}

Benefits of App Settings and Connection Strings in Azure Key Vault

Once this HashiCorp Terraform code is used to deploy the Azure App Service and Azure Key Vault, the application will be configured with the actual value of the App Setting and Connection String on the App Service / Web App instance being pulled securely from Azure Key Vault.

The App Settings and Connection Strings of an Azure App Service instance can be configured directly on the App Service. While you do not want to hard code secret values in the Terraform code, it’s perfectly fine to set the values of App Settings and Connection Strings directly. However, there are some benefits when pulling the values from Azure Key Vault Secrets instead.

When pulling the App Settings and Connection String values from Azure Key Vault Secrets, you will have the following benefits:

  • Centrally Managed – Azure Key Vault Secrets are stored in a single place, and can be referenced by multiple Azure App Service / Web App instances, Azure Functions, or other applications. If any secret values need to be shared across multiple applications / services that use the same value, then Azure Key Vault provides a central place to manage those secrets. This will make updating the secret values easier to do in a single place, rather than in all the various applications that might need to use it.
  • Control Who Can See Secrets – There may be times where a Developer or other Engineer must have access to the Azure App Service / Web App instance within Microsoft Azure. This could be to manage or just view the resource configuration. Just because they can access the Azure resource, doesn’t mean you want them to have access to the secrets, passwords, or connection string used by the application. By pulling App Setting and Connection String values from Azure Key Vault secrets, you can restrict access to the secrets separately from access to the App Service instance.

I hope this article helps you better deploy, manage, and secure Azure App Service instances and the secrets needed to run your applications.

Microsoft MVP

Chris Pietschmann is a Microsoft MVP, HashiCorp Ambassador, and Microsoft Certified Trainer (MCT) with 20+ years of experience designing and building Cloud & Enterprise systems. He has worked with companies of all sizes from startups to large enterprises. He has a passion for technology and sharing what he learns with others to help enable them to learn faster and be more productive.
HashiCorp Ambassador Microsoft Certified Trainer (MCT) Microsoft Certified: Azure Solutions Architect