Posted on 10 Comments

Pass variables values inside terraform modules

In this article I will explain two different ways to pass variables values inside terraform modules. Modules let you separate your code into small units and help the engineer structure its project better.

https://www.terraform.io/language/modules/syntax

Modules in terraform (terraform files) can be placed on folders and their location should be provided on the module directive. Lets say for example that you host a main.tf on your current working directory which should call two modules. The first module would be a storage account and the second would be an app service. Your main.tf file should look like below.

module "app_service_test" {
  source                  = "./modules/appservice"
}
module "storage_account_test" {
  source                  = "./modules/storageaccount"
}

However you want to pass some variables inside the child modules for example the resource group name, location etc.

First method – Define variables on root module

The first way you can pass variables inside your child modules would be to define a variables.tf file on your main module and a terraform.tfvars. Then you should also define a variables.tf file inside each module and contain the definition of each module.

terraform.tfvars (root module)

app_service_plan_name   = "ger-plan-test"
app_service_name        = "ger-site-test"
resource_group_name     = "geralexgr-terraform-rg"
resource_group_location = "West Europe"
storage_account_name    = "geralexgrsgv2"

variables.tf (root module)

variable "storage_account_name" {
  type        = string
  description = "Storage account name"
  default     = ""
}

variable "resource_group_name" {
  type        = string
  description = "RG name in Azure"
}

variable "app_service_plan_name" {
  type        = string
  description = "App Service Plan name in Azure"
}

variable "app_service_name" {
  type        = string
  description = "Name for the app service"
}

variable "resource_group_location" {
  type        = string
  description = "RG location in Azure"
}

variables.tf (storageaccount module)

variable "storage_account_name" {
    type        = string
    description = "Storage account name"
}
variable "resource_group_name" {
    type        = string
    description = "RG name in Azure"
}

variable "resource_group_location" {
    type        = string
    description = "RG location in Azure"
}

variables.tf (appservice module)

variable "app_service_plan_name" {
    type        = string
    description = "App Service Plan name in Azure"
}

variable "app_service_name" {
    type = string
    description = "Name for the app service"
}
variable "resource_group_name" {
    type        = string
    description = "RG name in Azure"
}

variable "resource_group_location" {
    type        = string
    description = "RG location in Azure"
}

Then on your main module you should call your child modules as follows:

module "app_service_test" {
  source                  = "./modules/appservice"
  app_service_plan_name   = var.app_service_plan_name
  app_service_name        = var.app_service_name
  resource_group_name     = var.resource_group_name
  resource_group_location = var.resource_group_location
}

module "storage_account_test" {
  source                  = "./modules/storageaccount"
  storage_account_name    = var.storage_account_name
  resource_group_name     = var.resource_group_name
  resource_group_location = var.resource_group_location
}

Second method – Pass variables on module call

With this approach you do not need to have variables.tf file and terraform.tfvars file inside your root module. You only need the definition as described above inside appservice and storageaccount folders (variables.tf).

variables.tf (appservice module)

variable "app_service_plan_name" {
    type        = string
    description = "App Service Plan name in Azure"
}

variable "app_service_name" {
    type = string
    description = "Name for the app service"
}
variable "resource_group_name" {
    type        = string
    description = "RG name in Azure"
}

variable "resource_group_location" {
    type        = string
    description = "RG location in Azure"
}

variables.tf (storageaccount module)

variable "storage_account_name" {
    type        = string
    description = "Storage account name"
}
variable "resource_group_name" {
    type        = string
    description = "RG name in Azure"
}
variable "resource_group_location" {
    type        = string
    description = "RG location in Azure"
}

Then your main.tf file should be:

module "app_service_test" {
  source                  = "./modules/appservice"
  app_service_plan_name   = "ger-plan-test"
  app_service_name        = "ger-site-test"
  resource_group_location = "West Europe"
  resource_group_name     = "geralexgr-terraform-rg"
}

module "storage_account_test" {
  source                  = "./modules/storageaccount"
  storage_account_name    = "geralexgrsgv2"
  resource_group_name     = "geralexgr-terraform-rg"
  resource_group_location = "West Europe"
}
Posted on Leave a comment

dynamically set dependsOn using variables – Azure devops

DependsOn is a condition on Azure devops with which you can define dependencies between jobs and stages.

An example can be found in the below picture where the stage2 depends from the production stage and will execute only when the production stage finishes. If the production stage fails, then the stage2 will not continue its execution.

The typical way to define a dependency would be by naming the stages and note on which stage you need your dependencies. For example in the stage2 we use dependsOn with the value stage1

stages:
- stage: stage1
  displayName: running stage1
  jobs:
  - job: job1
    displayName: running job1
    steps:
    - script: echo job1.task1
      displayName: running job1.task1  

- stage: stage2
  dependsOn: stage1
  displayName: running stage2
  jobs:
  - job: job2
    displayName: running job2
    steps:
    - script: echo job2.task1
      displayName: running job1.task1  

However you can also define dependsOn using a variable. This means that you can dynamically set under which stage another stage will depend and not by setting that as a static variable.

An example of this can be found below:

parameters:
  - name: myparam
    type: string
    values:
      - production
      - dev
      - qa

variables:
  ${{ if eq( parameters['myparam'], 'production' ) }}:
    myenv: production
  ${{ elseif eq( parameters['myparam'], 'dev' ) }}:
    myenv: dev
  ${{ elseif eq( parameters['myparam'], 'qa' ) }}:
    myenv: qa

trigger:
- none

pool:
  vmImage: ubuntu-latest

stages:
- stage: ${{ variables.myenv }}
  displayName: running ${{ variables.myenv }}
  jobs:
  - job: job1
    displayName: running job1
    steps:
    - script: echo job1.task1
      displayName: running job1.task1  

- stage: stage2
  dependsOn: ${{ variables.myenv }}
  displayName: running stage2
  jobs:
  - job: job2
    displayName: running job2
    steps:
    - script: echo job2.task1
      displayName: running job1.task1  

When we run the pipeline we will be asked for the environment as a parameter.

This parameter will be then passed into a variable and then this variable will be used for dependsOn condition.

You could also use the parameter itself as shown below.

- stage: stage2
  dependsOn: ${{ variables.myenv }}
  displayName: running stage2
  jobs:
  - job: job2
    displayName: running job2
    steps:
    - script: echo job2.task1
      displayName: running job1.task1  

Keep in mind that when you use variables, you should use the template syntax which is processed at compile time.

Youtube video:

Posted on 1 Comment

Parameters and variables – GitHub workflows

Variables and parameters can be used on GitHub workflows in order to provide input and store temporary values that should be passed on tasks. When you need to ask for user input one should use the parameters and when that is not necessary variables is the preferred way to go.

We will now examine how we can use both in a github workflow.

You can define an input parameter using the inputs keyword. Using the code below you can run a workflow manually. The input that is requested is a string and the description is the message that will be shown to the user.

  # Allows you to run this workflow manually from the Actions tab
  workflow_dispatch:
    inputs:
      username:
        description: "give me your username"
        default: "geralexgr"
        type: "string"

Then later in the workflow you can use the value of the input using inputs.username

The below job will show the input of the user:

  job1:
    runs-on: ubuntu-latest
    steps:
      - name: task inside job1
        run: |
          echo The username is ${{ inputs.username }}

Variables on the other hand can be used during runtime. Similar to using default environment variables, you can use custom environment variables in your workflow actions. To create a custom variable, you need to define it in your workflow file using the env context.

      - name: print variable
        env:
          NAME: "Gerasimos"
        run: |
          echo Variable name is: $NAME

During workflow run the variable will be printed on the output.

Youtube tutorial:

Posted on 2 Comments

Update variable group using Azure DevOps rest API – POSTMAN

I was struggling to update a variable group using the Azure DevOps Rest API. In this article I will document the procedure using POSTMAN.

First things first you should create a PAT in order to interact with the API. If you do not know how to create such a thing you should read my previous article about running a build through a REST api on which I documented also the creation of a PAT.

Then you will need to add the access token under authorization tab of POSTMAN using Type Basic Auth. The PAT should be added as plain text.

Then you will need to add Content-Type as application/json under Headers.

Then you will have to create your URL. This should be of the format:

https://dev.azure.com/Organization/project/_apis/distributedtask/variablegroups/groupVariableID?api-version=5.1-preview.1

Important: You should use the version=5.1-preview.1. If you use the latest version you will notice an error on the call. This is a bug that has not been fixed as I found online.

In my example I wanted to update the variable group with the ID 5 and add a variable named new-var. The body of your request should be like below. Keep in mind that we use the PUT HTTP verb to update the variable group. This means everything that is inside the variable group will be discarded. If you followed all the steps correctly you will notice the below output JSON. This should indicate success on the procedure.

Lastly you can locate your new variable inside the variable group.

Variablegroups – Update – REST API (Azure DevOps Task Agent) | Microsoft Docs

Video tutorial on YouTube: