Arrays in Azure App Service App Settings Configuration with Terraform
Setting up
The new ASP.NET Core Configuration is already setup and ready to go to load environment variables (checkout the default configuration). So if we create a simple ASP.NET Core Web API project, we can set some environment variables and our controller actions will response with the correct output.
If we go ahead and create a ASP.NET API from the templates by using dotnet new
dotnet new webapi
This will give us some basics to build on to. Now let’s just create an AppOptions
class to bind out configuration to and register it within our ConfigureServices
method within Startup.cs
.
public class AppOptions
{
public string Name { get; set; }
public string[] Names { get; set; }
}
public class Startup
{
public Startup(IConfiguration configuration) => Configuration = configuration;
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.Configure<AppOptions>(Configuration);
}
}
Next up is creating our controller, we’ll create a NamesController
to just output the values of the current AppOptions
.
[ApiController, Route("[controller]")]
public class NamesController : ControllerBase
{
[HttpGet]
public object Get([FromServices] IOptionsSnapshot<AppOptions> options)
{
return options.Value;
}
}
Now spin up the project with dotnet run
and then we can curl the /names
endpoint (or another tool of choice).
❯ curl http://localhost:5000/names
{"name":null,"names":null}
Now if we stop the .NET app and then set some variables for the child processes, we’ll be able to see these in the API response.
Below is setting the variables using bash, however, if you’re in windows you can use $env:Name=xxx
in PowerShell.
export NAME=TestName
export NAMES__0=TestName1
export NAMES__1=TestName2
export NAMES__2=TestName3
dotnet run
Now let’s curl the endpoint and see our data get returned.
curl http://localhost:5000/names
{"name":"TestName","names":["TestName1","TestName2","TestName3"]}
The configuration library inside ASP.NET Core is doing a few magical things here, It’s converting our __0
, __1
, __3
in to items within our Names
array at the given indexes. It’s actually possible to just not pass an index at all and pass any string at the end of __
and it will map it in to the array. Check out the below for an example.
export NAMES__NotAnIndex=TestNameHere
For more information what is going off behind the scenes you can read more on the Microsoft Docs site.
Terraform Configuration
Now let’s look how we’d configure our Terraform scripts to allow variables passed in by .tfvar
files and how we can map these to our app_settings
configuration block.
We’ll start by taking the sample terraform configuration from the provider docs. This will create us a resource group, app service plan and an app service.
resource "azurerm_resource_group" "example" {
name = "example-resources"
location = "West Europe"
}
resource "azurerm_app_service_plan" "example" {
name = "example-appserviceplan"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
sku {
tier = "Standard"
size = "S1"
}
}
resource "azurerm_app_service" "example" {
name = "example-app-service"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
app_service_plan_id = azurerm_app_service_plan.example.id
site_config {
dotnet_framework_version = "v4.0"
scm_type = "LocalGit"
}
app_settings = {
"SOME_KEY" = "some-value"
}
}
Now we want to add a terraform list variable of names
we can do this by adding the following to our terraform script
variable "names" {
type = list(string)
description = "List of names."
}
Now we can convert our previous terraform script to use the names
variable to create a map of strings that will be used for the app_settings instead.
resource "azurerm_app_service" "example" {
name = "example-app-service"
app_settings = {for idx, val in var.names: "NAMES__${idx}" => val}
}
The above is using a for expression to create a map from a list with the index of the item appended in the key of NAMES__
.
Let’s create create a terraform.tfvars
with our list of names.
names = [
"Kevin",
"Sakis",
"Irene",
]
Now we can do a terraform plan
to see what it would create for us.
As you can see our names
variable has been flattened in to our app_settings in the correct format for ASP.NET Core Configuration.
This is perfect so far but we are most likely going to have other settings in our applications that we need to merge together with our list of names. Here we can pull our for expression up in to a locals block and use a merge function. This will take 2 maps and merge them together.
locals {
appsettings = merge({
"APPLICATION__SETTING1" = "Value1"
"APPLICATION__SETTING2" = "Value2"
}, {for idx, val in var.names: "NAMES__${idx}" => val})
}
Now we’ll just have to update our azurerm_app_service
resource to reference the locals.
resource "azurerm_app_service" "example" {
name = "example-app-service"
app_settings = local.appsettings
}
Now when we do another terraform plan
we’ll see both lots of appsetttings merged together.