Terraform enables the definition, preview, and deployment of cloud infrastructure. Using Terraform, you create configuration files using HCL syntax. The HCL syntax allows you to specify the cloud provider - such as Azure - and the elements that make up your cloud infrastructure. After you create your configuration files, you create an execution plan that allows you to preview your infrastructure changes before they're deployed.
In Azure, most resources live inside a resource group. Resource groups are containers that hold related resources for a solution - they define the scope for access control, policy, and cost management. Some resource types can exist at subscription, management group, or tenant scope, but you cannot create a virtual machine, database, or storage account without first placing it in a resource group.
This makes resource groups the first thing you create in any Azure Terraform project. Getting the structure right early saves you from painful reorganizations later. This guide covers how to create resource groups, apply tags and locks, and organize them for real-world projects.
Provider Configuration and Prerequisites
Before any resource group can be provisioned, Terraform must be able to authenticate to Azure and resolve provider plugins.
Prerequisites for a Terraform Azure resource group project include:
- Terraform 1.0 or later
- Azure CLI installed and authenticated
- An Azure subscription
- Basic familiarity with Terraform HCL
Authentication is performed via the Azure CLI. In your terminal, use the Azure CLI to setup your account permissions locally.
az login
Your browser will open and prompt you to enter your Azure login credentials. After successful authentication, your terminal will display your subscription information.
For Windows PowerShell installation of Azure CLI:
Invoke-WebRequest -Uri https://aka.ms/installazurecliwindows -OutFile .\AzureCLI.msi; Start-Process msiexec.exe -Wait -ArgumentList '/I AzureCLI.msi /quiet'; rm .\AzureCLI.msi
The provider block for Azure is defined in HCL.
provider "azurerm" {
features {}
}
A full terraform block with required providers is commonly used:
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 4.0"
}
azuread = {
source = "hashicorp/azuread"
version = "~> 3.0"
}
}
}
provider "azurerm" {
features {}
}
provider "azuread" {}
Initialize Terraform to download required provider plugins.
terraform init
This initializes Terraform, downloads required provider plugins, and prepares your project for execution.
Creating a Basic Resource Group
The name and location are required for azurermresourcegroup.
A simple resource group example:
resource "azurerm_resource_group" "main" {
name = "rg-myapp-prod-eastus"
location = "East US"
tags = {
Environment = "production"
Project = "myapp"
ManagedBy = "terraform"
}
}
Another example from reference configuration:
resource "azurerm_resource_group" "KingRG" {
name = "KingRG"
location = "Central US"
}
Resource groups are the organizational backbone of most Azure deployments. Getting the naming, tagging, and access control right at this level pays dividends as your infrastructure grows.
Group-level dashboards make it easy to see the status of all resources belonging to a particular application or team, and alert on issues before they affect users.
Tags are applied at the resource group level to provide cost allocation, environment classification, and ownership metadata. The tags block accepts key value pairs such as Environment, Project, and ManagedBy.
Resource Group Naming and Tagging Strategies
Naming conventions directly impact discoverability and automation.
Common naming pattern: rg-{application}-{environment}-{region}
The name must be unique within the subscription and adhere to Azure naming rules.
Location values must match Azure supported locations, e.g., East US, Central US.
Tags enable cost management and governance. Use Terraform's for_each to create consistent groups across environments.
Importing Existing Azure Resources into Terraform
Importing allows an existing manually created resource to be brought under Infrastructure as Code management without recreation.
The guide walks through importing an existing Azure Resource Group and Virtual Network into Terraform.
Prerequisites for import:
- Azure CLI installed and authenticated
- Terraform installed on your machine
- Visual Studio Code for writing Terraform configurations
- An existing Azure Resource Group and Virtual Network created manually
Create a new directory for your Terraform project. Inside the directory, create a new file named main.tf and define the provider block for Azure.
Add the Terraform configuration for your Resource Group and Virtual Network:
resource "azurerm_resource_group" "KingRG" {
name = "KingRG"
location = "Central US"
}
resource "azurerm_virtual_network" "KingVnet" {
name = "KingVnet"
location = azurerm_resource_group.KingRG.location
resource_group_name = azurerm_resource_group.KingRG.name
address_space = ["10.0.0.0/16"]
}
Navigate to your Terraform project directory in the terminal and run:
terraform init
To import the existing KingRG Resource Group into Terraform state:
Retrieve the resource group’s Azure ID:
Go to the Azure Portal → Resource Groups → Click on KingRG. Select Json Template and copy the id field.
Run the following command to import the resource:
terraform import azurerm_resource_group.KingRG "/subscriptions/YOUR_SUBSCRIPTION_ID/resourceGroups/KingRG"
Replace YOURSUBSCRIPTIONID with your actual subscription ID.
Verify the import by running:
terraform state list
Next, import the existing KingVnet Virtual Network:
Retrieve the Virtual Network’s Azure ID:
Go to the Azure Portal → Virtual Networks → Click on KingVnet. Select Jason Template and copy the id field.
Run the following command:
terraform import azurerm_virtual_network.KingVnet "/subscriptions/YOUR_SUBSCRIPTION_ID/resourceGroups/KingRG/providers/Microsoft.Network/virtualNetworks/KingVnet"
Verify the import:
terraform state list
Now that the resources are imported, run:
terraform plan
This will show any differences between your Terraform configuration and the actual Azure state.
Import Resource Group and Import Virtual Network steps allow Verify and Manage Resources workflows to be established.
Drift Detection and State Management
With Terraform, you can then discover and manage drift between your desired state and the current state of the infrastructure.
State management complexity is a known disadvantage. The terraform resource management is dependent on a state file used for monitoring changes. The management of the state file can be complex, notwithstanding the fact that it tends to get complicated in larger environments, or more engagements that involve more teams which are working on the same infrastructure.
Drift Management considerations:
Terraform is able to spot the differences occurring between the desired and the real states for resources, but it may not always be able to rebuild the infrastructure with the same commands that created it initially.
Disadvantages of Using Terraform for Azure Resource Groups
While Terraform gives most advantages for managing Azure Resource Groups, there are also some disadvantages to keep in mind:
- Learning Curve: It require domain-specific language (HCL) and configuration syntax which is unique and at times challenging for new learning especially when the new things are being introduced(IaC).
- State Management Complexity: The terraform resource management is dependent on a state file used for monitoring changes. The management of the state file can be complex, notwithstanding the fact that it tends to get complicated in larger environments, or more engagements that involve more teams which are working on the same infrastructure.
- Vendor Lock-in: Even if it is cross-cloud compatible, Terraform has a possibility of vendor lock-in while hosting your instance. Moving from Terraform for Azure infrastructure provisioning, which apparently involved a great deal of configuration, may become a nightmare. You may not be able to quickly migrate even to another IaC tool or cloud provider as a result of this.
- Drift Management: Terraform is able to spot the differences occurring between the desired and the real states for resources, but it may not always be able to rebuild the infrastructure with the same commands that created it initially
These constraints affect real-world adoption decisions for platform teams.
Tutorial Workflow for Azure Resource Group Creation
In this tutorial, you will create a Terraform configuration to deploy an Azure resource group. This resource group is the foundation for the infrastructure you will build in the subsequent tutorials.
An Azure subscription is required. If you do not have an Azure account, create one now. This tutorial can be completed using only the services included in an Azure free account.
If you are using a paid subscription, you may be charged for the resources needed to complete the tutorial.
You will use the Azure CLI to authenticate with Azure.
Open your PowerShell prompt as an administrator and run the following command:
$ Invoke-WebRequest -Uri https://aka.ms/installazurecliwindows -OutFile .\AzureCLI.msi; Start-Process msiexec.exe -Wait -ArgumentList '/I AzureCLI.msi /quiet'; rm .\AzureCLI.msi
Terraform must authenticate to Azure to create infrastructure.
In your terminal, use the Azure CLI to setup your account permissions locally.
$ az login
Your browser will open and prompt you to enter your Azure login credentials. After successful authentication, your terminal will display your subscription information.
You have logged in
Real-World Organization Patterns
Resource groups define the scope for access control, policy, and cost management.
Use Terraform's for_each to create consistent groups across environments, apply delete locks to production groups, and set budgets to avoid cost surprises. Most application resources you build in Azure depend on this foundation being solid.
Group-level dashboards make it easy to see the status of all resources belonging to a particular application or team, and alert on issues before they affect users.
Summary
Resource groups are the organizational backbone of most Azure deployments. Getting the naming, tagging, and access control right at this level pays dividends as your infrastructure grows. Use Terraform's for_each to create consistent groups across environments, apply delete locks to production groups, and set budgets to avoid cost surprises. Most application resources you build in Azure depend on this foundation being solid.
Conclusion
Azure resource group provisioning via Terraform represents a foundational control plane operation where naming, location, tagging, and import semantics converge. The execution plan preview capability provides safety before deployment, while state file dependency introduces operational complexity at scale. Provider configuration with azurerm ~> 4.0 and authentication via az login establishes the trust boundary for all subsequent resource declarations.
Import workflows allow brownfield adoption without resource recreation, using Azure Resource IDs retrieved from the Portal Json Template view. Drift detection complements import by surfacing configuration divergence between HCL desired state and live Azure state.
Disadvantages such as learning curve for HCL, state management complexity in multi-team environments, potential vendor lock-in, and imperfect drift reconciliation remain relevant considerations for platform engineering decisions. Naming conventions like rg-myapp-prod-eastus combined with consistent tags for Environment, Project, and ManagedBy enable cost allocation and governance at scale.
Getting the structure right early saves painful reorganizations later. Resource groups remain the first construct in any Azure Terraform project because virtual machines, databases, and storage accounts cannot exist without first being placed in a resource group.