Terraform Azure Provider
Introduction
The Azure Provider for Terraform is an essential tool that allows you to create, manage, and update Microsoft Azure resources through Terraform's declarative configuration files. This provider acts as a bridge between Terraform and the Azure API, enabling you to treat your Azure infrastructure as code.
With the Azure Provider, you can manage virtually all Azure services—from virtual machines and storage accounts to complex resources like Kubernetes clusters—using the same consistent workflow regardless of the resource type. This approach offers numerous benefits:
- Version control for your infrastructure
- Consistent deployments across environments
- Automated testing and validation of changes
- Dependency management between resources
Let's explore how to configure and use the Azure Provider to deploy resources to Azure.
Installing and Configuring the Azure Provider
Prerequisites
Before using the Terraform Azure Provider, you need:
- Terraform CLI installed (version 0.14.9 or newer)
- Azure subscription
- Authentication credentials for Azure
Provider Configuration
To use the Azure Provider, you need to declare it in your Terraform configuration. The following example shows a basic provider configuration:
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 3.0"
}
}
}
provider "azurerm" {
features {}
}
This configuration:
- Specifies the Azure Resource Manager (azurerm) provider
- Uses version 3.0 or newer (but not 4.0 or newer)
- Enables the default feature set with the empty
features {}
block
Authentication Methods
The Azure Provider supports several authentication methods:
- Azure CLI (easiest for development)
- Service Principal with Client Secret
- Service Principal with Client Certificate
- Managed Identity
Azure CLI Authentication
If you've already authenticated using the Azure CLI (az login
), Terraform can use those credentials automatically:
provider "azurerm" {
features {}
# No credentials needed - uses Azure CLI authentication
}
Service Principal Authentication
For automated workflows or CI/CD pipelines, a Service Principal is recommended:
provider "azurerm" {
features {}
subscription_id = "your-subscription-id"
client_id = "your-app-id"
client_secret = "your-client-secret"
tenant_id = "your-tenant-id"
}
It's generally better to provide these values using environment variables rather than hardcoding them:
provider "azurerm" {
features {}
# Credentials will be read from environment variables:
# ARM_SUBSCRIPTION_ID
# ARM_CLIENT_ID
# ARM_CLIENT_SECRET
# ARM_TENANT_ID
}
Deploying Your First Azure Resource
Let's create a simple resource group as our first Azure deployment.
Resource Group Example
provider "azurerm" {
features {}
}
resource "azurerm_resource_group" "example" {
name = "example-resources"
location = "East US"
tags = {
environment = "Development"
project = "Terraform Learning"
}
}
To deploy this resource:
-
Initialize Terraform:
bashterraform init
-
Preview the changes:
bashterraform plan
Output (simplified):
Terraform will perform the following actions:
# azurerm_resource_group.example will be created
+ resource "azurerm_resource_group" "example" {
+ id = (known after apply)
+ location = "eastus"
+ name = "example-resources"
+ tags = {
+ "environment" = "Development"
+ "project" = "Terraform Learning"
}
}
Plan: 1 to add, 0 to change, 0 to destroy. -
Apply the changes:
bashterraform apply
After confirming the apply operation, Terraform will create the resource group in Azure.
Understanding Azure Provider Resources
The Azure Provider organizes resources according to Azure's resource model. Here's a simple diagram showing this organization:
Most Azure resources in Terraform follow this pattern where resources are grouped into resource groups.
Commonly Used Azure Resources
Here are some commonly used Azure resources you can manage with Terraform:
Virtual Network
resource "azurerm_virtual_network" "example" {
name = "example-network"
resource_group_name = azurerm_resource_group.example.name
location = azurerm_resource_group.example.location
address_space = ["10.0.0.0/16"]
}
resource "azurerm_subnet" "example" {
name = "internal"
resource_group_name = azurerm_resource_group.example.name
virtual_network_name = azurerm_virtual_network.example.name
address_prefixes = ["10.0.2.0/24"]
}
Storage Account
resource "azurerm_storage_account" "example" {
name = "examplestorageacc"
resource_group_name = azurerm_resource_group.example.name
location = azurerm_resource_group.example.location
account_tier = "Standard"
account_replication_type = "LRS"
}
Virtual Machine
resource "azurerm_linux_virtual_machine" "example" {
name = "example-vm"
resource_group_name = azurerm_resource_group.example.name
location = azurerm_resource_group.example.location
size = "Standard_B1s"
admin_username = "adminuser"
network_interface_ids = [
azurerm_network_interface.example.id,
]
admin_ssh_key {
username = "adminuser"
public_key = file("~/.ssh/id_rsa.pub")
}
os_disk {
caching = "ReadWrite"
storage_account_type = "Standard_LRS"
}
source_image_reference {
publisher = "Canonical"
offer = "UbuntuServer"
sku = "18.04-LTS"
version = "latest"
}
}
Working with Azure Provider Data Sources
Data sources allow you to fetch information about existing Azure resources. This is useful when you need to reference resources that:
- Were created outside of Terraform
- Are managed by a different Terraform configuration
- Need to be queried dynamically
Example: Using Data Sources
# Get information about an existing resource group
data "azurerm_resource_group" "example" {
name = "existing-resource-group"
}
# Use information from the data source
resource "azurerm_virtual_network" "example" {
name = "example-network"
location = data.azurerm_resource_group.example.location
resource_group_name = data.azurerm_resource_group.example.name
address_space = ["10.0.0.0/16"]
}
Complete Project Example: Web Server with Database
Let's walk through a more complete example that provisions a web server with a database backend:
provider "azurerm" {
features {}
}
# Resource Group
resource "azurerm_resource_group" "example" {
name = "example-resources"
location = "East US"
}
# Virtual Network
resource "azurerm_virtual_network" "example" {
name = "example-network"
address_space = ["10.0.0.0/16"]
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
}
# Subnet
resource "azurerm_subnet" "example" {
name = "internal"
resource_group_name = azurerm_resource_group.example.name
virtual_network_name = azurerm_virtual_network.example.name
address_prefixes = ["10.0.2.0/24"]
}
# Network Interface
resource "azurerm_network_interface" "example" {
name = "example-nic"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
ip_configuration {
name = "internal"
subnet_id = azurerm_subnet.example.id
private_ip_address_allocation = "Dynamic"
public_ip_address_id = azurerm_public_ip.example.id
}
}
# Public IP
resource "azurerm_public_ip" "example" {
name = "example-pip"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
allocation_method = "Dynamic"
}
# Virtual Machine
resource "azurerm_linux_virtual_machine" "example" {
name = "example-vm"
resource_group_name = azurerm_resource_group.example.name
location = azurerm_resource_group.example.location
size = "Standard_B1s"
admin_username = "adminuser"
network_interface_ids = [
azurerm_network_interface.example.id,
]
admin_ssh_key {
username = "adminuser"
public_key = file("~/.ssh/id_rsa.pub")
}
os_disk {
caching = "ReadWrite"
storage_account_type = "Standard_LRS"
}
source_image_reference {
publisher = "Canonical"
offer = "UbuntuServer"
sku = "18.04-LTS"
version = "latest"
}
custom_data = base64encode(<<-EOF
#!/bin/bash
apt-get update
apt-get install -y apache2
echo "Hello from Terraform Azure Provider Tutorial" > /var/www/html/index.html
EOF
)
}
# Database Server
resource "azurerm_mysql_server" "example" {
name = "example-mysqlserver"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
administrator_login = "mysqladmin"
administrator_login_password = "H@Sh1CoR3!" # Better to use variables or secrets management
sku_name = "B_Gen5_1"
storage_mb = 5120
version = "5.7"
ssl_enforcement_enabled = true
}
# Database
resource "azurerm_mysql_database" "example" {
name = "exampledb"
resource_group_name = azurerm_resource_group.example.name
server_name = azurerm_mysql_server.example.name
charset = "utf8"
collation = "utf8_unicode_ci"
}
# Output the public IP address
output "public_ip_address" {
value = azurerm_public_ip.example.ip_address
}
This configuration:
- Creates a resource group
- Sets up networking (VNet, subnet, NIC, public IP)
- Deploys a virtual machine running Apache
- Provisions a MySQL database server and database
- Outputs the public IP address of the VM
Best Practices for Azure Provider
1. Use Variables for Reusability
variable "location" {
description = "Azure region to deploy resources"
default = "East US"
}
variable "resource_prefix" {
description = "Prefix for resource names"
default = "tfexample"
}
resource "azurerm_resource_group" "example" {
name = "${var.resource_prefix}-rg"
location = var.location
}
2. Organize with Modules
Break complex configurations into reusable modules:
module "web_server" {
source = "./modules/web_server"
resource_group_name = azurerm_resource_group.example.name
location = azurerm_resource_group.example.location
subnet_id = azurerm_subnet.example.id
}
3. Use Terraform Backend for State Management
terraform {
backend "azurerm" {
resource_group_name = "terraform-state-rg"
storage_account_name = "tfstate12345"
container_name = "tfstate"
key = "prod.terraform.tfstate"
}
}
4. Enable Provider Features Explicitly
provider "azurerm" {
features {
virtual_machine {
delete_os_disk_on_deletion = true
}
key_vault {
purge_soft_delete_on_destroy = true
}
}
}
Troubleshooting Common Issues
Authentication Problems
If you encounter authentication errors:
- Verify your credentials are correct
- Check that your Service Principal has the necessary permissions
- Ensure environment variables are properly set
# Set these variables for Service Principal authentication
export ARM_CLIENT_ID="00000000-0000-0000-0000-000000000000"
export ARM_CLIENT_SECRET="12345678-0000-0000-0000-000000000000"
export ARM_SUBSCRIPTION_ID="12345678-0000-0000-0000-000000000000"
export ARM_TENANT_ID="12345678-0000-0000-0000-000000000000"
Resource Creation Failures
When resources fail to create:
- Check the error message in the Terraform output
- Verify that resource names meet Azure requirements
- Ensure you have quota for the requested resources
- Check that dependencies exist and are correctly referenced
Rate Limiting
When hitting API rate limits:
provider "azurerm" {
features {}
# Add rate limiting settings
client_timeouts {
read = "30m"
}
}
Summary
The Azure Provider for Terraform enables you to manage Azure resources using infrastructure as code. Key takeaways:
- The Azure Provider bridges Terraform and Azure API
- Multiple authentication methods are available, with Service Principal being ideal for automation
- Resources are organized according to Azure's resource model
- Data sources allow you to reference existing Azure resources
- Best practices include using variables, modules, and proper state management
With the Azure Provider, you can create, update, and delete Azure resources in a consistent, repeatable way, ensuring your infrastructure is maintainable and version-controlled.
Additional Resources
To continue your learning journey:
- Official Terraform Azure Provider Documentation
- Microsoft Learn Terraform Modules
- Terraform on Azure GitHub Examples
Practice Exercises
- Create a Terraform configuration that deploys a static website using Azure Storage Account static website hosting.
- Modify the web server example to include an Azure Application Gateway as a load balancer.
- Build a module that creates a fully configured Azure Kubernetes Service (AKS
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)