Terraform – Initial Setup

Terraform init setup

Terraform is the go-to choice for Infrastructure as Code (IaC) due to its simplicity and effectiveness. You have the flexibility to create the initial setup in various ways. In this guide, I'm presenting an approach that is both adaptable and production-ready, seamlessly integrating with CI/CD pipelines.


main.tf - main file with resource definition

resource "azurerm_resource_group" "rg" {
  name     = var.name
  location = var.location
  tags     = merge(var.tags, local.default_tags)

provider.tf - place to specify all version required

terraform {
  required_version = ">= 1.5"
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = ">= 3.90.0"

  backend "azurerm" {}

provider "azurerm" {
  features {}

Having backend "azurerm" {} it allows to provide backend credentials as a environment variables during execution

variables.tf - Definition of interface - input variables (not values)

variable "name" {
  description = "The name of the resource group"
  type        = string

variable "location" {
  description = "The location of the resource group"
  type        = string

variable "tags" {
  description = "A map of tags to add to the resource group"
  type        = map(string)

variables.auto.tfvars - values of the variables defined in variables.tf file

name     = "rg1"
location = "eastus"
tags = {
  environment = "dev"

All *.auto.tfvars files are being loaded during execution without using -var-file flag

output.tf - definition of output interface

output "name" {
  value       = azurerm_resource_group.rg.name
  description = "The name of the resource group"

locals.tf - local default values or local functions to make main.tf file more human readable

locals {
  default_tags = {
    manneged_by = "Terraform"
    costcenter  = "12345"

.env - credential to connect to backend and manage provisioner's resources.

export ARM_CLIENT_ID="23a023..."
export ARM_CLIENT_SECRET="NumaYEPkxG..."
export ARM_TENANT_ID="2c29-..."
export ARM_SUBSCRIPTION_ID="bfff..."
export TF_STATE_BLOB_KEY="test.tfstate"
export ARM_ACCESS_KEY="access_key"

This approach offers the following advantages:

  1. .env can be replace in any time, this allow as to use another environment
  2. many file can be easly track in git
  3. update of provisioner version taking place in separate file which doesn't touch the resources configuration
  4. backend/provisioner variables can be set during execution, as a consequence, it allows to use that code in CI/CD pipelines



At the beginning, SPN with appropriate role (contributor) needs to be created

# Login to Azure
az login

# Create a new Service Principal
az ad sp create-for-rbac --name <name>

This command will output JSON with the appIddisplayNamenamepassword, and tenant.

# Cole assigment
az role assignment create --assignee <app_id> --role Contributor --subscription <subscription_id>


Second point is Backend - you will store a state file there

# Resource Group
az group create --name <resource-group-name> --location <location>

# Storage account 
az storage account create --name <storage-account-name> --resource-group <resource-group-name> --location <location> --sku Standard_LRS

# get storage account key
az storage account keys list --account-name <storage-account-name> --resource-group <resource-group-name> --query '[0].value' --output tsv

# Container
az storage container create --name <container-name> --account-name <storage-account-name> --account-key <account-key>

.env file

For local development is useful to have that file. You can find that information in previous paragraphs


Execution is typical

# Install provisioners
terraform init

# dry run
terraform plan

# Provisioning
terraform apply

Summary of initial setup

The provided instructions give a solid starting point for diving into Terraform, with practical examples guiding users through file setup, backend configuration, and execution of essential commands.

Read more

Pulumi - Terraform's competitor

Leave a Reply

Your email address will not be published. Required fields are marked *