Eduarn – Online & Offline Training with Free LMS for Python, AI, Cloud & More

Thursday, April 16, 2026

End-to-End Azure Governance with Terraform: Users, Groups, RBAC & Policy

 

Managing access and governance in Azure can quickly become complex without automation. Using Terraform, we can build a scalable and repeatable setup that includes identity management, access control, and policy enforcement.

In this blog, we implement:

  • Azure AD Users (dynamic with for_each)
  • Azure AD Groups (Admin & Tester)
  • RBAC Role Assignments
  • Azure Storage Account
  • Azure Policy Enforcement

🧩 Architecture Overview

Azure AD Users → Azure AD Groups → RBAC → Azure Resource

Azure Policy


👤 Step 1: Create Users using for_each

We define multiple users using a Terraform map and dynamically create them:

variable "users" {
  type = map(object({
    user_principal_name = string
    display_name        = string
    password            = string
  }))
}

This allows scalable identity creation.


👥 Step 2: Create Groups

We define two groups:

  • Admin Group → Full access
  • Tester Group → Read-only access

🔐 Step 3: Assign Users to Groups

Each user is mapped to a group, following best practices of group-based access control.


🏗️ Step 4: Deploy Azure Resource

We create:

  • Resource Group
  • Storage Account

🔑 Step 5: RBAC (Access Control)

Roles are assigned at the resource level:

  • Admin Group → Storage Account Contributor
  • Tester Group → Storage Blob Data Reader

This ensures least privilege access.


📜 Step 6: Azure Policy (Governance)

We enforce a policy:

✔ Only allow Standard_LRS storage accounts

"effect": "deny"

This prevents non-compliant resources.

 

 Full Code:

 terraform {

required_providers {
azurerm = {
source = "hashicorp/azurerm"
}
azuread = {
source = "hashicorp/azuread"
}
}
}

provider "azurerm" {
features {}
}

provider "azuread" {}

# -------------------------------------------------
# USERS (FOREACH)
# -------------------------------------------------
variable "users" {
type = map(object({
user_principal_name = string
display_name = string
password = string
}))

default = {
user1 = {
user_principal_name = "user1_demo@eduarng.com"
display_name = "User One Demo"
password = "TempPassword@12345!"
}

user2 = {
user_principal_name = "user2_demo@eduarng.com"
display_name = "User Two Demo"
password = "TempPassword@12345!"
}
}
}

# -------------------------------------------------
# CREATE USERS
# -------------------------------------------------
resource "azuread_user" "users" {
for_each = var.users

user_principal_name = each.value.user_principal_name
display_name = each.value.display_name
password = each.value.password
force_password_change = true
}

# -------------------------------------------------
# ADMIN GROUP
# -------------------------------------------------
resource "azuread_group" "admin_group" {
display_name = "Admin-Group"
security_enabled = true
}

# -------------------------------------------------
# TESTER GROUP
# -------------------------------------------------
resource "azuread_group" "tester_group" {
display_name = "Tester-Group"
security_enabled = true
}

# -------------------------------------------------
# GROUP MEMBERSHIP
# -------------------------------------------------
resource "azuread_group_member" "user1_admin" {
group_object_id = azuread_group.admin_group.object_id
member_object_id = azuread_user.users["user1"].object_id
}

resource "azuread_group_member" "user2_tester" {
group_object_id = azuread_group.tester_group.object_id
member_object_id = azuread_user.users["user2"].object_id
}

# -------------------------------------------------
# RESOURCE GROUP
# -------------------------------------------------
resource "azurerm_resource_group" "demo_rg" {
name = "rg-aad-rbac-policy-demo"
location = "East US"
}

# -------------------------------------------------
# STORAGE ACCOUNT
# -------------------------------------------------
resource "azurerm_storage_account" "storage" {
name = "aadstoragedemo12345"
resource_group_name = azurerm_resource_group.demo_rg.name
location = azurerm_resource_group.demo_rg.location
account_tier = "Standard"
account_replication_type = "LRS"
}

# -------------------------------------------------
# RBAC FOR ADMIN GROUP
# -------------------------------------------------
resource "azurerm_role_assignment" "admin_rbac" {
scope = azurerm_storage_account.storage.id
role_definition_name = "Storage Account Contributor"
principal_id = azuread_group.admin_group.object_id
}

# -------------------------------------------------
# RBAC FOR TESTER GROUP
# -------------------------------------------------
resource "azurerm_role_assignment" "tester_rbac" {
scope = azurerm_storage_account.storage.id
role_definition_name = "Storage Blob Data Reader"
principal_id = azuread_group.tester_group.object_id
}

# -------------------------------------------------
# AZURE POLICY DEFINITION
# -------------------------------------------------
resource "azurerm_policy_definition" "storage_policy" {
name = "restrict-storage-sku-policy"
policy_type = "Custom"
mode = "All"
display_name = "Allow only Standard_LRS Storage Accounts"

policy_rule = jsonencode({
if = {
allOf = [
{
field = "type"
equals = "Microsoft.Storage/storageAccounts"
},
{
field = "Microsoft.Storage/storageAccounts/sku.name"
notEquals = "Standard_LRS"
}
]
}
then = {
effect = "deny"
}
})
}

# -------------------------------------------------
# POLICY ASSIGNMENT
# -------------------------------------------------
resource "azurerm_resource_policy_assignment" "storage_policy_assign" {
name = "storage-policy-assignment"

resource_id = azurerm_storage_account.storage.id

policy_definition_id = azurerm_policy_definition.storage_policy.id
}

🎯 WHAT CHANGED

✔ user1 → Admin Group
✔ user2 → Tester Group
✔ RBAC roles aligned:

  • Admin → Contributor-like access
  • Tester → Read-only access

🎯 Final Outcome

With a single Terraform file, we achieve:

✔ Identity management (Azure AD)
✔ Access control (RBAC)
✔ Resource deployment
✔ Governance enforcement (Azure Policy)


💡 Conclusion

This setup reflects a real-world enterprise model where:

  • Access is controlled via groups
  • Permissions follow least privilege
  • Policies enforce compliance

Using Terraform ensures everything is automated, consistent, and reusable.

No comments:

Post a Comment

End-to-End Azure Governance with Terraform: Users, Groups, RBAC & Policy

  Managing access and governance in Azure can quickly become complex without automation. Using Terraform, we can build a scalable and repeat...