.env?

So you have a project which needs environment variables, and now you want to run it locally. Setting environment variables manually is cumbersome. Another option is to source a file with those variables, but you will need to do that for every terminal session, and what if you weren’t using a terminal in the first place? That’s exacly what .env files are for. You create an env files which holds your local test values, and your application will pick those up when it runs. Pretty hand, right?

Terraform?

Terraform is an infrastructure-as-code tool. You simply define how your infrastructure should look like, use the correct Terraform provider and let it take care of spinning up, updating, and destroying the needed resources. Explaining Terraform itself in details is out of the scope of this post, but you can find tons of posts and tutorials about it.

Both?

Now let’s say that you want to define an Azure Container Group resource but with some secure environment variables. We can do something like this

resource "azurerm_container_group" "example" {
    name = "example"
    ... few other things

    container {
        ... few other things
        secure_environment_variables = {
            CONNECTION_STRING = "<the connection string>"
        }
    }
}

We have two problems here:

  1. the connection string is included in the resource, we’ll have to repeat it for other resources
  2. the string may contain a username and a password, which is never something we want to check in with our source control

We can easily solve the first one by using variables, but the second isn’t as straightforward. You’d expect Terraform to support something like that out-of-the-box but unfortunately it doesn’t. So I found this solution on Stackoverflow

locals {
    dotenv = { for tuple in regexall("(.*)=(.*)", file(".env")) : tuple[0] => tuple[1] }
}

This solution is actually brilliant. It’s pure Terraform, concise, and gets the job done. Actually, kind of gets the job done. If let’s say your variable has “=” anywhere then it’ll take the last ‘=’ as the separator.

However, a very small but non-standard modification can help

locals {
    dotenv = { for tuple in regexall("(.*) = (.*)", file(".env")) : tuple[0] => tuple[1] }
}

We just added spaces around the ‘=’ in the regex, and we’ll also need to add spaces around ‘=’, and that just solves it. Keep in mind that it only superficially solves that particular problem. It’s not a general-purpose solution.