Azure Terraform tutorial
Tutorial: Writing a main.tf
File for an Azure Project with Terraform
The main.tf
file is often the core of your Terraform configuration, serving as the primary blueprint for the infrastructure you want to provision. It’s where you define the resources, modules, and providers that Terraform will manage.
1. Understanding the Role of main.tf
In any Terraform project, the files with the .tf
extension in your current working directory are considered part of the root module. When you run terraform init
, terraform plan
, or terraform apply
in a directory, Terraform treats that directory as the root module. While you can name your Terraform files anything ending in .tf
, main.tf
is a common convention for the primary configuration file.
The goal of Terraform’s declarative language in main.tf
is to describe the desired state of your infrastructure, and Terraform will determine the steps to achieve that state.
2. Basic Structure of a main.tf
File
A typical main.tf
file for an Azure project will include at least two main blocks: the terraform
block and the provider
block, followed by resource definitions.
# main.tf
# 1. Terraform Block: Configuration for Terraform itself
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 3.0" # Specify a compatible version
}
}
}
# 2. Provider Block: Configuration for Azure
provider "azurerm" {
features {} # The 'features' block is required
}
# 3. Resource Block(s): Define your Azure infrastructure resources
# Example: Azure Resource Group
resource "azurerm_resource_group" "example" {
name = "my-resource-group"
location = "West US" # Or another Azure region like "westeurope"
}
Let’s break down each part:
a. The terraform
Block
This block configures Terraform’s behavior, especially specifying the required providers.
required_providers
: Within this, you declare theazurerm
provider, specifying itssource
(where Terraform can find it) and aversion
constraint (e.g.,~> 3.0
means any version 3.x.x, but not 4.x.x). Specifying a version is a best practice to prevent automatic upgrades with breaking changes.
b. The provider "azurerm"
Block
This block configures the Azure provider itself.
features {}
: This empty block is mandatory for theazurerm
provider and can be used to enable or disable optional features. Authentication for the Azure provider is typically handled via environment variables, service principals, or Azure Cloud Shell, rather than directly in this block.
c. Resource Block(s)
This is where you define the actual infrastructure components you want to create in Azure.
resource "azurerm_resource_group" "example"
:resource
: Keyword indicating a resource definition.azurerm_resource_group
: The type of resource to create (an Azure Resource Group).example
: A local name (or identifier) for this resource within your Terraform configuration. This name is used to reference the resource elsewhere in your code.
- Inside the resource block, you define arguments like
name
andlocation
. These specify the desired properties of the Azure Resource Group.
3. Defining Additional Azure Resources
You can add more resource blocks to your main.tf
file to build out your Azure infrastructure. For instance, creating a Virtual Network within the resource group:
# ... (terraform and provider blocks as above) ...
resource "azurerm_resource_group" "example" {
name = "my-resource-group"
location = "West US"
}
resource "azurerm_virtual_network" "example" {
name = "my-virtual-network"
address_space = ["10.0.0.0/16"]
location = azurerm_resource_group.example.location # Reference the location from the resource group
resource_group_name = azurerm_resource_group.example.name # Reference the name from the resource group
}
Notice how location
and resource_group_name
in the azurerm_virtual_network
block reference attributes from the azurerm_resource_group.example
resource. This demonstrates dependencies in Terraform, where one resource relies on another.
4. Using Variables for Dynamic Configuration
To make your main.tf
configuration more dynamic and reusable, you should avoid hard-coding values and instead use variables. While variables are typically declared in a variables.tf
file, their values are often used in main.tf
.
Let’s assume you have a variables.tf
file:
# variables.tf
variable "resource_group_name" {
description = "The name of the resource group"
type = string
default = "my-dynamic-resource-group"
}
variable "azure_region" {
description = "The Azure region to deploy resources"
type = string
default = "eastus"
}
Then, your main.tf
would reference these variables:
# main.tf (updated with variables)
# ... (terraform and provider blocks) ...
resource "azurerm_resource_group" "example" {
name = var.resource_group_name # Uses the variable
location = var.azure_region # Uses the variable
}
resource "azurerm_virtual_network" "example" {
name = "${var.azure_region}-vnet" # Using interpolation for dynamic naming
address_space = ["10.0.0.0/16"]
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
}
You can provide values for these variables via command line (-var
), a .tfvars
file, or environment variables.
5. Defining Outputs to Expose Information
Output variables are useful for displaying important information about your infrastructure after deployment or for passing values between modules or CI/CD pipelines. Outputs are often defined in an outputs.tf
file, but can also be in main.tf
.
# main.tf (updated with an output)
# ... (all previous blocks) ...
output "resource_group_id" {
description = "The ID of the created Resource Group"
value = azurerm_resource_group.example.id
}
output "virtual_network_name" {
description = "The name of the created Virtual Network"
value = azurerm_virtual_network.example.name
}
The value
attribute typically references an attribute of a resource that Terraform has provisioned.
6. Executing Your main.tf
Configuration
Once your main.tf
(and any associated variables.tf
, outputs.tf
) is ready, you’ll use the Terraform CLI to execute it.
-
Initialize Terraform: Navigate to your project directory in the terminal and run:
terraform init
This command initializes the working directory, downloads the specified providers, and sets up the backend for state management.
-
Generate an Execution Plan: Review the changes Terraform proposes to make without actually applying them:
terraform plan
This command creates an execution plan, showing what resources will be created, modified, or destroyed.
-
Apply the Changes: If the plan looks correct, apply the changes to your Azure account:
terraform apply
This command executes the actions outlined in the plan, provisioning your infrastructure. You will typically be prompted for confirmation before changes are made.
7. Best Practices for main.tf
- Readability: Keep your
main.tf
clean and organized. Well-formatted code is easier for all contributors to understand and maintain. Theterraform fmt
command can automatically format your code. - Modularity: As your project grows, consider breaking down your infrastructure into Terraform modules. Modules are reusable components that can be called from your root
main.tf
. This preventsmain.tf
from becoming too large and complex. - Version Control: Always store your
main.tf
and other configuration files in a version control system like Git. This allows for tracking changes, collaboration, and rollbacks.
By following these steps, you can effectively write and manage your Azure infrastructure using a main.tf
file with Terraform.