terraform-azure-bootstrap

Part I of building a Modern Data Platform in Azure using Terraform

Through a series of posts, we will go from nothing more than a machine that meets the minimum requirements for the azure-terraform-bootstrap to a fully functioning Enterprise-class Modern Data Platform. We will start this series here, walking through the terraform-azure-bootstrap project.

What is terraform-azure-bootstrap? Well, to put it simply, it’s a git repo created to help initialize Azure for use as a Terraform backend.

Why would you want to use terraform-azure-bootstrap or any bootstrapping mechanism for Terraform?

  • By default, Terraform creates its state file on the local machine that Terraform is being executed on. This makes it difficult to share the state file across multiple users of Terraform. Locking in a specific user to update specific resources. In short, using local state files does not work well in a team environment.
  • Second, storing the Terraform state file locally.
  • Third, Terraform state files can save sensitive data; using a remote backend based in cloud storage like S3 or Azure Blob Storage allows automatically encrypts the data at rest and in transit.
  • Fourth, again by default, Terraform uses the user’s credentials to affect change delivered by the Terraform code. This requires the user to execute the code with an account that has elevated permission.

How does terraform-azure-bootstrap solves these issues by:

  • Uses Azure Storage to store the state file
  • Uses env.tfvar files in the bootstrap to declare separate State Containers and State key values
  • Uses an App Registration SPN account as the credentials for making modifications to the Azure environment, not the user’s credentials, and allows the users to follow least permission access security best practice.
  • Uses Azure Key Vault to hold the credentials of the Storage Account that holds the state as well as the credentials of the SPN that is used to execute the Terraform code on Azure.

In addition, because state location information is stored in a file and referenced by the bootstrap, the solution allows you to use the same Terraform code for multiple environments. For example, the same Terraform code can be executed for development, test, and production while keeping the state container name and state key different for each environment. It does this by using .tfvars files specific to each environment, loaded by the TerrafromAzureBootstrap.sh, which initializes the project, passing these values along with the state storage account to the Terraform Init command.

Lets make a quick review of the requirements:

You have a machine that can run bash scripts and has the AZ CLI installed, and you have access to Azure Subscription with owner permissions as well as Azure AD permissions to create an App Registration. Lastly of course, you need access to the git repo: https://github.com/sfibich/terraform-azure-bootstrap

The terraform-azure-bootstrap project consists of two shell scripts, ConfigureAzureForSecureTerraformAccess.sh and TerraformAzureBootstrap.sh and an Example folder. We will take a look at each script individually as well as the example provided in the repository.

ConfigureAzureForSecureTerraformAccess.sh

The ConfigureAzureForSecureTerraformAccess.sh is the first script and only needs to be run once for each Azure Environment. This script creates four objects in Azure, a resource group, which contains an Azure Key Vault (AKV) and an Azure Storage Account to be used for blob storage. Lastly, it creates an App Registration.

The App Registration is named Terraform; the Key Vault is named with a random prefix and the suffix “-terraform-kv”. The storage account is given the same random prefix as the key vault and the suffix “terraform”. Finally, the Resource Group is named terraform-mgmt-rg. These naming conventions are then used by the Boostrap shell script to locate the resources during its execution. In addition to creating these objects, the script went ahead and loaded five secrets to the Key Vault. These secrets match the Terraform Azurerm backend provider configuration variables when authenticating with a Service Principal using a Client Id and Client Secret. (https://www.terraform.io/docs/language/settings/backends/azurerm.html)

  • ARM-TENANT-ID – The Tenant Id of the Tenant you ran the ConfigureAzureForSecureTerraformAccess.sh script. This is also the tenant that all of the objects were created in.
  • ARM-SUBSCRIPTION-ID – The Subscription Id that you were logged in to when you ran the ConfigureAzureForSecureTerraformAccess.sh. This is also the subscription in which all of the objects were created.
  • ARM-CLIENT-ID – The Client Id of the App Registration that was created by the ConfigureAzureForSecureTerraformAccess.sh script.
  • ARM-CLIENT-SECRET – The Client Secret of the App Registration that was created by the ConfigureAzureForSecureTerraformAccess.sh script.
  • ARM-ACCESS-KEY – The Primary Storage Account Key for the Azure Storage Account that was created by the ConfigureAzureForSecureTerraformAccess.sh script.

TerraformAzureBootstrap.sh

TerraformAzureBootstrap.sh is the script that will be run prior to the execution of Terraform when you are switching Terraform projects or when you are switching environments. In a nutshell, it loads the previously mentioned Terraform Azurerm configuration five variables in the current session. It also loads two values from the environment tfvars file passed the container name and key value to be used by Terrform to manage the state. Terraform state values such as Storage Account, Container, and Key cannot be passed in at runtime as dynamic variables and must be static if they are set in Terraform files. The bootstrapping gets around this limitation of Terraform by allowing you to use the same Terraform code for development, test, and production by passing different environment tfvars files to the terraformAzureBootstrap.sh shell script. The shell script reads all the protected information from the key vault and the environment state location values in the tfvar files and calls “terraform init” using backend-config switches to set the backend for that particular terraform project/environment combination.

The diagram above shows a client machine, either a developer machine or a build agent running TerraformAzureBootstrap.sh. It retrieves all of the information in its different steps it then initializes the back end on the storage account.

  1. Script pulls Terraform Azurerm the five configuration values from the Azure Key Vault
    1. ARM-TENANT-ID
    2. ARM-SUBSCRIPTION-ID
    3. ARM-CLIENT-ID
    4. ARM-CLIENT-SECRET
    5. ARM-ACCESS-KEY
  2. Interrogates the terraform-mgmt-rg resource group for a storage account name that has the suffix of “terraform”
  3. Loads the Container and Key names which will be used to hold Terraform State information
  4. Initializes the Terraform backend to the storage account using the blob container name and blob name found in the var file. You are now ready to run terraform apply!

Example

There is a very simple example script included with the terraform-azure-bootstrap repository. All this example does is create a resource group with a few tags. The value of this example is to show you how you can run the same script into three different environments and have the state file saved in three different Terraform State Key locations without changing any code. Running the sample code below after running the ConfigureAzureForSecureTerraformAccess.sh once will produce three different resource groups. Each tied to its own state file. You can then go back to any of the previous environments by re-running bootstrap for the specific environment and altering it. Terraform will use the correct state file. Go ahead and try it out.

source ../TerraformAzureBootstrap.sh -f dev/dev.tfvars
terraform apply -var-file dev/dev.tfvars

source ../TerraformAzureBootstrap.sh -f test/test.tfvars
terraform apply -var-file test/test.tfvars

source ../TerraformAzureBootstrap.sh -f prod/prod.tfvars
terraform apply -var-file prod/prod.tfvars
Results after running through the example Code

Useful Links:

Scroll to Top