Terraform as Infrastructure as Code solution for deploying on 2 Cloud services.

terraform-as-infrastructure-as-code-solution-for-deploying-on-2-cloud-services

In the previous lab (See), we have learn how to deploy solely on Azure using ARM and later Bicep, which are powerful and stable tools. But they have one crucial disadvantage. ARM and Bicep only works for Azure. When dealing with large solution you may want to leverage multiple providers to get the most out of the various services. This also applies even if your company decide to stick solely to the Microsoft universe. Why is that? Well, you probably wants to host your code somewhere? Just to give an example. Maybe Azure DevOps, GitHub, GitLab or similiar. Please mind that Azure DevOps is neither supported by ARM nor by Bicep. So let’s take our case to the next level.

In this lab, we will create a new Azure DevOps project and a storage account using Terraform. Please mind! For simplification, Azure DevOps and Azure were chosen as 2 providers but we could also deploy some AWS, Google Cloud or other resources.

Our Storage Account Explorer from the last lab was quite a big hit, we got at least 5 users right from the start and the number is likely to grow. In addition, management was quite impressed by Infrastructure as Code. Your team and you are now entrusted with the mission to bring Infrastructure as Code to every new feature projects. Even the project setup is meant to be done by Infrastructure as Code. The idea is: if a new Proof-of-Concept project got approved, the respective team just need to run „the“ script and a new DevOps project is created along with a few resources on Azure. This should ease and speed up the deployment process for new projects significantly.

Exercise 1: Deploying on Azure using Terraform

Task 1: Setup project

  1. Open https://shell.azure.com  to open the cloudshell in fullscreen mode.
  2. Switch to Bash if it is not already the case.
  3. Now let’s setup a new project.
mkdir lab-tf
cd lab-tf

touch variables.tf
touch providers.tf
touch main-azurerm.tf
touch main-azuredevops.tf
touch output.tf

code .

Task 2: Setup Azure provider

The terraform.exe itself is just a runner and interpreter of the written configuration script. The features and support for Azure and other services is written in the providers. Even for generating random numbers and string, you would need install respective providers. e.g. random

Let’s add Azure Cloud support for our project by adding azurerm:

  1. Open providers.tf (you may find the complete solution for step 2 – 4 below but try to find the solution by yourself for a better learning experience).
  2. Add a new terraform block at the beginning of the file.
  3. Inside the terraform block, add azurerm to the list of required_providers (see docs)
  4. Add a new provider block for at the end azurerm

The providers.tf file should look like this at the end:

# Configuring terraform including the list of required_providers
terraform {
  required_providers { 
    azurerm = {
      source = "hashicorp/azurerm"
      # optionally you could specify a version
      # version = "..."
    }
  } 
}

# Configure the Microsoft Azure Provider "azurerm"
provider "azurerm" { 
  features {} 
}

We can now ask terraform to download and add the provider azurerm to the project:

  1. Run terraform init at the root of your project folder <your-path>/lab-tf/.
    • Terraform connects to the Terraform Registry to download the azurerm provider plugin. It ensures that the version of the provider matches the constraints specified in the configuration if specified.
  2. Notice that terraform has
    • added a new folder .terraform/hashicorp/azurerm. In which it stores the downloaded provider
    • added a lock file `.terraform.lock.hcl`. This is a crucial file generated by Terraform 0.14 and later versions. It records the exact versions and hashes of the provider plugins that Terraform has installed. This lock file ensures that Terraform consistently uses the same versions of providers on subsequent runs, which helps in maintaining the consistency and reproducibility of your environments.

Task 3: Adding Azure resources

  1. Now that the provider are setup, we can start adding Azure resources in our script. The designated file is main-azurerm.tf. (You can find the complete solution below)
    • We need to add an azurerm_resource_group resource block (see docs) and a azurerm_storage_account resource block (see docs).
    • As with Bicep and ARM in the previous lab, we would want Terraform to generate some random string for us, that can be used as storage account name. Terraform does not offer a similiar uniqueString function but a random_string resource block (see docs)
  2. You need to run terraform init again as your code now have additional dependencies on a new provider – namely random.
    • Notice, that providers are used as prefix in Terraform such as azurerm_ and random_.
  3. Run terraform plan -out .tfplan to create an execution plan.
    • Terraform should show a extensive preview of the actions it would make if you apply these changes.
  4. Run terraform apply .tfplan to apply the changes in the plan.
  5. Try to modify the script so that it enable the static_website service (see Docs).
  6. Repeat step 5 and 6.
resource "azurerm_resource_group" "draphony" {
    name        = "azlab-iac-tf"
    location    = "westeurope"
}

resource "random_string" "draphony" {
    keepers = {
      rgid = azurerm_resource_group.draphony.id
    }
    length      = 13
    special     = false
    upper       = false
}

resource "azurerm_storage_account" "draphony" {
    name                        = "draphony${random_string.draphony.id}"
    location                    = azurerm_resource_group.draphony.location
    account_replication_type    = "LRS"
    account_tier                = "Standard"
    resource_group_name         = azurerm_resource_group.draphony.name
}

Bonus: Modify the output.tf so that it outputs the keys from the storage account

  1. For sensitive informaiton such as the keys, you will need to mark it as sensitive in the newer version. This will just affect the output in the CLI but not the capability of other modules and services (such as Azure DevOps) to access the output.

Exercise 2: Deploying on Azure DevOps using Terraform

Now that our storage account is up and running on Azure, we need to add Azure DevOps to our project.

Task 1: Adding Azure DevOps resources to your Terraform project

Please mind that you need an Azure DevOps account for this part. You can create a free account here. Terraform will use a Personal Access Token to gain access, therefore you can use any email / account to signup.

  1. As with Azure, we need to setup the Azure DevOps provider to work with Azure DevOps.
  2. Open providers.tf in your project folder.
  3. Modify the content of the file by adding the Azure DevOps provider to the required_providers and add and new provider block for azuredevops as shown below:
terraform {
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
    }
    
    azuredevops = {
      source = "microsoft/azuredevops"
    }
  }
}

provider "azurerm" {
  features {}
}

provider "azuredevops" {
  personal_access_token = "TO_BE_REPLACED"
  org_service_url       = "TO_BE_REPLACED"
}
  1. Go to Azure DevOps (here) and acquire a PAT with full scope and the org_service_url and replace it in the providers.tf
    • What is a PAT? To attain a Personal Access Token (PAT) in Azure DevOps, you need to follow these steps. A Personal Access Token is used to authenticate into Azure DevOps services and is necessary for scenarios like automated processes, or when using the Azure DevOps REST API
    • Once logged in, click on your profile picture in the upper right corner of the Azure DevOps portal.
    • From the dropdown menu that appears after clicking your profile picture, select „Personal access tokens“. This will redirect you to the Personal Access Tokens page.
    • On the Personal Access Tokens page, click on “New Token” or “+ New Token” to create a new PAT.
  2. Open and modify main-azuredevops.tf in the project so that it deploys:
    1. a new Azure DevOps project Awesome Project (See doc)
    2. an additional Uninitialized git repository webapp (See doc)
    3. all repositories should only allow commits from authors from „draphony.com“ and „draphony.de“.
  3. Run terraform plan -out .tfplan to create an execution plan.
  4. Run terraform apply .tfplan to apply the changes.
resource "azuredevops_project" "draphony" {
  name               = "Awesome Project"
  description        = "You did it man!"
  visibility         = "private"
  version_control    = "Git"
  work_item_template = "Scrum"
}

resource "azuredevops_git_repository" "draphony" {
  project_id     = azuredevops_project.draphony.id
  name           = "webapp"

  initialization {
    init_type = "Uninitialized"
  }
}

resource "azuredevops_repository_policy_author_email_pattern" "draphony" {
  project_id            = azuredevops_project.draphony.id
  enabled               = true
  blocking              = true
  author_email_patterns = ["*@draphony.com", "*@draphony.de"]
}

Task 2: Adding variables and for_each

Let’s add some configurability to our configuration script by allowing the amount of repository to be configurable.

  1. Open the variables.tf file (the complete file can be found at the end).
  2. Add a new variable block draphony_git_repos, which
    • has the default value set to ["webapp", "api"].
    • has type set to set(string)
  3. Open the main-azuredevops.tf (the complete file can be found at the end) Modify the azuredevops_git_repository resource block:
    • use a for_each on var.draphony_git_repos
    • change the property name to the respective value of the iteration.
  4. Run terraform plan -out .tfplan to create an execution plan.
    • Terraform should suggest to destroy webapp repository and recreate it along with the new repository api. Because the reference variable azuredevops_git_repository.draphony is now an dictionary azuredevops_git_repository.draphony["webapp"] and azuredevops_git_repository.draphony["api"].
    • Bonus: You can use terraform state mv to reflect the latest changes. (see docs)
  5. Run terraform plan -out .tfplan -var "draphony_git_repos=[\"webapp\", \"api\", \"ui-tests\"]"  to create an execution plan.
    • Again, Terraform should suggest to destroy webapp repository and recreate it along with the new repositories api and ui-tests unless you did the bonus.
    • Bonus: use a variable definitions file .tfvars to set the value (see docs)
  6. Run terraform apply .tfplan to apply the changes.
# variables.tf
variable "draphony_git_repos" {
  type    = set(string)
  default = ["webapp", "api"]
}
# main-azuredevops.tf
resource "azuredevops_project" "draphony" {
  name               = "Awesome Project"
  description        = "You did it man!"
  visibility         = "private"
  version_control    = "Git"
  work_item_template = "Scrum"
}

resource "azuredevops_git_repository" "draphony" {
  for_each       = var.draphony_git_repos
  project_id     = azuredevops_project.draphony.id
  name           = each.key

  initialization {
    init_type = "Uninitialized"
  }
}

resource "azuredevops_repository_policy_author_email_pattern" "draphony" {
  project_id            = azuredevops_project.draphony.id
  enabled               = true
  blocking              = true
  author_email_patterns = ["*@draphony.com", "*@draphony.de"]
}

Exercise 3: Clean up using Terraform

  1. Run terraform destroy at the root of your project folder `<your-path>/lab-tf/` to teardown everything.

Conclusion and further reading

As you can see, Terraform is quite easy to learn yet quite powerful. It is quite a popular tool that you find in many companies and projects, which do Infrastructure as Code. Some even use „terraforming“ instead of Infrastructure as Code giving the expression that it is almost defacto standard.

Nevertheless, there are also other interesting alternatives such as

  1. Bicep and ARM from Microsoft. Native tool for Azure Deployment. Comparable very stable.
  2. Pulumi. Which allows you to use programming languages such as C#, Typescript, Python, Java and others to define your Infrastructure as Code.
  3. Theses are just a few, there are many mores

Checkout other labs and courses from Draphony to learn more about Terraform:

  1. Deploy Hub-Spoke network topology using Terraform (coming soon)
  2. AZ-400 Designing and Implementing Microsoft DevOps Solutions

If you enjoyed the lab or have any questions / feedback, please leave us a comment.

Tác giả

  • Azure

Newsletter zu Aktionen

Trage dich ein um keine Aktionen von uns zu verpassen.
Wir senden 1-2 E-Mails pro Quartal.