Azure security starts with identity. Microsoft Entra ID (formerly Azure AD) manages users and applications. RBAC (Role-Based Access Control) controls who can do what. Managed identities give your applications secure access without secrets. In this lesson, you'll learn the IAM fundamentals that secure every Azure deployment.

1. Learning Objectives

By the end of this lesson, you will be able to:

  • Understand Microsoft Entra ID (Azure AD) and its components
  • Create and manage users, groups, and service principals
  • Assign RBAC roles at different scopes (management group, subscription, resource group)
  • Use managed identities for Azure resources
  • Implement conditional access policies
  • Audit access with Azure Monitor and Log Analytics

2. Why This Matters

Real-world scenario: A developer accidentally deletes a production database. Without proper RBAC, they had delete permissions. With least privilege, they only have read access. Azure IAM is your first line of defense against accidents and attacks.

3. Core Concepts

Azure Identity Components

Azure Identity Components:

┌─────────────────────────────────────────────────────────────────┐
│  MICROSOFT ENTRA ID (formerly Azure AD)                         │
│  • Cloud identity service                                       │
│  • Stores users, groups, applications                           │
│  • Provides authentication and authorization                    │
│  • Tenants: isolated instance of Entra ID                      │
└─────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────┐
│  USERS                                                          │
│  • Individual people or service accounts                        │
│  • Can be cloud-only or synchronized from on-prem AD           │
│  • Each user has unique sign-in credentials                     │
└─────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────┐
│  GROUPS                                                         │
│  • Collection of users                                          │
│  • Assign RBAC roles to groups, not individuals                 │
│  • Types: Security groups, Microsoft 365 groups                 │
└─────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────┐
│  SERVICE PRINCIPALS                                             │
│  • Identity for applications and automation                     │
│  • Used by Azure DevOps, GitHub Actions, Terraform              │
│  • Authenticates with client secret or certificate              │
└─────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────┐
│  MANAGED IDENTITIES                                             │
│  • Azure-managed service principals                             │
│  • No credentials to manage (more secure)                       │
│  • Types: System-assigned (1:1 with resource), User-assigned    │
└─────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────┐
│  RBAC ROLES                                                     │
│  • Define what actions are allowed on resources                 │
│  • Built-in roles (Owner, Contributor, Reader)                  │
│  • Custom roles for specific needs                              │
└─────────────────────────────────────────────────────────────────┘
Azure identity components

Azure CLI Setup

# Install Azure CLI
# Linux
curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash

# macOS
brew update && brew install azure-cli

# Windows (choco)
choco install azure-cli

# Login to Azure
az login

# List subscriptions
az account list --output table

# Set active subscription
az account set --subscription "My Subscription"

# Create a user (requires Entra ID admin permissions)
az ad user create \
  --display-name "Alice Developer" \
  --user-principal-name alice@yourdomain.onmicrosoft.com \
  --mail-nickname alice \
  --password "TempPassword123!"

# Create a group
az ad group create \
  --display-name "Developers" \
  --mail-nickname developers \
  --description "Development team"

# Add user to group
az ad group member add \
  --group "Developers" \
  --member-id $(az ad user show --id alice@yourdomain.onmicrosoft.com --query id -o tsv)

# List groups
az ad group list --output table

# List users
az ad user list --output table
Azure CLI installation and identity commands

RBAC Roles and Scopes

RBAC Scopes (hierarchy):

┌─────────────────────────────────────────────────────────────────┐
│                    MANAGEMENT GROUP                              │
│  (Multiple subscriptions, enterprise-wide policies)             │
│                           │                                      │
│                           ▼                                      │
│                    SUBSCRIPTION                                  │
│  (Billing boundary, resource container)                         │
│                           │                                      │
│                           ▼                                      │
│                    RESOURCE GROUP                                │
│  (Logical container for resources)                              │
│                           │                                      │
│                           ▼                                      │
│                     RESOURCE                                     │
│  (Individual resource: VM, Storage, SQL)                        │
└─────────────────────────────────────────────────────────────────┘

Built-in RBAC Roles:
┌─────────────────────────────────────────────────────────────────┐
│ Role               │ Description                                │
├────────────────────┼─────────────────────────────────────────────┤
│ Owner              │ Full access, including role assignment     │
│ Contributor        │ Full access except role assignment         │
│ Reader             │ View everything, no changes                │
│ User Access Admin  │ Manage role assignments                    │
│ Virtual Machine    │ Manage VMs, but not network/storage        │
│ Contributor        │                                             │
│ Storage Blob Data  │ Read/write blob data in storage accounts   │
│ Owner              │                                             │
│ Key Vault Secrets  │ Manage secrets in Key Vault                │
│ User               │                                             │
└────────────────────┴─────────────────────────────────────────────┘
RBAC scopes and built-in roles
# RBAC commands

# Assign role to user at subscription level
az role assignment create \
  --assignee alice@yourdomain.onmicrosoft.com \
  --role "Reader" \
  --scope /subscriptions/$(az account show --query id -o tsv)

# Assign role to group at resource group level
az role assignment create \
  --assignee "$(az ad group show --group Developers --query id -o tsv)" \
  --role "Contributor" \
  --resource-group my-resource-group

# List role assignments for a user
az role assignment list \
  --assignee alice@yourdomain.onmicrosoft.com \
  --output table

# Remove role assignment
az role assignment delete \
  --assignee alice@yourdomain.onmicrosoft.com \
  --role "Reader" \
  --scope /subscriptions/$(az account show --query id -o tsv)

# List built-in roles
az role definition list --output table

# Create custom role
cat > custom-reader.json << 'EOF'
{
  "Name": "Custom VM Reader",
  "Description": "Can read VM information but cannot modify",
  "Actions": [
    "Microsoft.Compute/virtualMachines/read",
    "Microsoft.Compute/virtualMachines/instanceView/read",
    "Microsoft.Network/networkInterfaces/read",
    "Microsoft.Network/publicIPAddresses/read"
  ],
  "NotActions": [],
  "DataActions": [],
  "NotDataActions": [],
  "AssignableScopes": ["/subscriptions/$(az account show --query id -o tsv)"]
}
EOF

az role definition create --role-definition custom-reader.json
RBAC role assignment commands

Service Principals (App Registration)

# Create a service principal (application identity)
az ad sp create-for-rbac \
  --name "my-automation-app" \
  --role Contributor \
  --scopes /subscriptions/$(az account show --query id -o tsv)

# Output:
# {
#   "appId": "12345678-1234-1234-1234-123456789012",
#   "displayName": "my-automation-app",
#   "password": "very-long-secret-string",
#   "tenant": "12345678-1234-1234-1234-123456789012"
# }

# Create service principal without default assignment
az ad sp create-for-rbac --name "my-app" --skip-assignment

# Create service principal with certificate authentication
az ad sp create-for-rbac \
  --name "cert-app" \
  --certificate @/path/to/cert.pem

# Get service principal details
az ad sp list --display-name "my-automation-app"

# Get service principal ID
SP_ID=$(az ad sp list --display-name "my-automation-app" --query "[0].id" -o tsv)

# Assign role to service principal
az role assignment create \
  --assignee $SP_ID \
  --role "Reader" \
  --resource-group my-resource-group

# Use service principal to login (CI/CD)
az login --service-principal \
  --username "12345678-1234-1234-1234-123456789012" \
  --password "very-long-secret-string" \
  --tenant "12345678-1234-1234-1234-123456789012"

# Or use environment variables
export AZURE_CLIENT_ID="12345678-1234-1234-1234-123456789012"
export AZURE_TENANT_ID="12345678-1234-1234-1234-123456789012"
export AZURE_CLIENT_SECRET="very-long-secret-string"
az login
Service principal creation and management

Managed Identities

# Enable system-assigned managed identity on a VM
az vm identity assign \
  --name my-vm \
  --resource-group my-rg

# Enable system-assigned managed identity on App Service
az webapp identity assign \
  --name my-app \
  --resource-group my-rg

# Get the principal ID of the managed identity
IDENTITY_ID=$(az vm show --name my-vm --resource-group my-rg --query identity.principalId -o tsv)

# Assign RBAC role to the managed identity (allow VM to read from Key Vault)
az role assignment create \
  --assignee $IDENTITY_ID \
  --role "Key Vault Secrets User" \
  --scope /subscriptions/$(az account show --query id -o tsv)/resourceGroups/my-rg/providers/Microsoft.KeyVault/vaults/my-vault

# Create user-assigned managed identity
az identity create \
  --name my-user-identity \
  --resource-group my-rg

# Get identity details
az identity show --name my-user-identity --resource-group my-rg

# Assign user-assigned identity to VM
az vm identity assign \
  --name my-vm \
  --resource-group my-rg \
  --identities $(az identity show --name my-user-identity --resource-group my-rg --query id -o tsv)
Managed identity configuration
# Using managed identity from Azure VM (Python example)
from azure.identity import DefaultAzureCredential
from azure.storage.blob import BlobServiceClient

# DefaultAzureCredential automatically uses managed identity when available
credential = DefaultAzureCredential()

# Connect to storage using managed identity
blob_service_client = BlobServiceClient(
    account_url="https://mystorageaccount.blob.core.windows.net",
    credential=credential
)

# List containers
containers = blob_service_client.list_containers()
for container in containers:
    print(container.name)

# Connect to Key Vault
from azure.keyvault.secrets import SecretClient

key_vault_url = "https://my-key-vault.vault.azure.net"
secret_client = SecretClient(vault_url=key_vault_url, credential=credential)

# Get secret
secret = secret_client.get_secret("my-secret")
print(secret.value)
Using managed identities in application code

Conditional Access

Conditional Access Policies (Entra ID P1/P2 required):

Common Conditional Access Rules:

1. Require MFA for all users
   ├── Assignments: All users
   ├── Cloud apps: All cloud apps
   ├── Conditions: None
   └── Grant: Require multi-factor authentication

2. Block access from risky locations
   ├── Assignments: All users
   ├── Conditions: Locations (named locations)
   ├── Grant: Block access
   └── Exclude: Trusted IPs (office network)

3. Require compliant devices
   ├── Assignments: All users
   ├── Conditions: Device platforms (iOS, Android, Windows)
   ├── Grant: Require compliant device
   └── Require approved client app

4. Block legacy authentication
   ├── Assignments: All users
   ├── Conditions: Client apps (Exchange ActiveSync, Other clients)
   ├── Grant: Block access
   └── Note: Blocks POP, IMAP, SMTP

Azure CLI doesn't directly manage Conditional Access. Use PowerShell or Portal:

# PowerShell example (Az module)
Connect-MgGraph -Scopes "Policy.Read.All", "Policy.ReadWrite.ConditionalAccess"

$caPolicy = New-MgIdentityConditionalAccessPolicy -DisplayName "Require MFA"
# ... additional configuration
Conditional Access policy patterns

4. Complete Project: Azure IAM Audit Tool

#!/usr/bin/env python3
"""
azure-iam-audit.py - Azure IAM security audit tool
"""

from azure.identity import DefaultAzureCredential
from azure.mgmt.authorization import AuthorizationManagementClient
from azure.mgmt.resource import ResourceManagementClient
from azure.mgmt.subscription import SubscriptionClient
from datetime import datetime, timedelta
import json

class AzureIAMAudit:
    def __init__(self):
        self.credential = DefaultAzureCredential()
        self.subscription_id = None
        self.authorization_client = None

    def set_subscription(self, subscription_id):
        self.subscription_id = subscription_id
        self.authorization_client = AuthorizationManagementClient(
            credential=self.credential,
            subscription_id=subscription_id
        )

    def list_subscriptions(self):
        """List available subscriptions"""
        client = SubscriptionClient(self.credential)
        subscriptions = client.subscriptions.list()
        print("Available Subscriptions:")
        for sub in subscriptions:
            print(f"  - {sub.display_name} ({sub.subscription_id})")
        return subscriptions

    def audit_role_assignments(self):
        """Audit all role assignments in subscription"""
        print("\n" + "=" * 80)
        print("AZURE RBAC ROLE ASSIGNMENTS")
        print("=" * 80)

        assignments = self.authorization_client.role_assignments.list()

        high_risk_roles = ['Owner', 'Contributor', 'User Access Administrator']

        for assignment in assignments:
            # Get role definition
            role = self.authorization_client.role_definitions.get_by_id(
                assignment.role_definition_id
            )

            # Get principal details (user, group, or service principal)
            principal_type = "Unknown"
            principal_name = assignment.principal_id

            print(f"\n📋 Assignment:")
            print(f"   Role: {role.role_name}")
            print(f"   Principal: {principal_name}")
            print(f"   Scope: {assignment.scope}")

            if role.role_name in high_risk_roles:
                print(f"   ⚠️ HIGH RISK ROLE")

    def audit_custom_roles(self):
        """List custom RBAC roles"""
        print("\n" + "=" * 80)
        print("CUSTOM RBAC ROLES")
        print("=" * 80)

        roles = self.authorization_client.role_definitions.list(
            filter="type eq 'CustomRole'"
        )

        for role in roles:
            print(f"\n🔐 Custom Role: {role.role_name}")
            print(f"   Description: {role.description}")
            print(f"   Actions: {role.permissions[0].actions if role.permissions else 'None'}")
            print(f"   NotActions: {role.permissions[0].not_actions if role.permissions else 'None'}")

    def audit_service_principals(self):
        """Audit service principals (requires Graph API)"""
        print("\n" + "=" * 80)
        print("SERVICE PRINCIPALS")
        print("=" * 80)
        print("Note: Use Azure Portal or Graph API for detailed SP audit")
        print("Check for:")
        print("  - Service principals with Owner/Contributor roles")
        print("  - Credentials older than 90 days")
        print("  - Unused service principals")

    def generate_report(self):
        """Generate complete security report"""
        print("\n" + "#" * 80)
        print("AZURE IAM SECURITY AUDIT REPORT")
        print(f"Generated: {datetime.now().isoformat()}")
        print(f"Subscription: {self.subscription_id}")
        print("#" * 80)

        self.audit_role_assignments()
        self.audit_custom_roles()
        self.audit_service_principals()

        print("\n" + "=" * 80)
        print("RECOMMENDATIONS")
        print("=" * 80)
        print("1. Enable Azure AD Privileged Identity Management (PIM) for admin roles")
        print("2. Use managed identities instead of service principals with secrets")
        print("3. Implement conditional access policies (MFA, location-based)")
        print("4. Remove unused service principals and role assignments")
        print("5. Use Azure Policy to enforce allowed role types")
        print("6. Enable diagnostic settings for Entra ID logs")

if __name__ == "__main__":
    audit = AzureIAMAudit()

    # List subscriptions
    subs = audit.list_subscriptions()

    # Select first subscription
    subscription_id = input("\nEnter subscription ID to audit: ")
    audit.set_subscription(subscription_id)

    audit.generate_report()
Complete Azure IAM security audit tool

5. Common Errors & Solutions

6. Summary Checklist

7. Next Steps

Next Azure lessons to generate:

  • Lesson 10.2: Azure Virtual Machines (Compute)
  • Lesson 10.3: Azure Storage & Blob
  • Lesson 10.4: Azure Networking (VNet)
  • Lesson 10.5: Azure SQL & Cosmos DB (Databases)
  • Lesson 10.6: Azure Functions & App Services (Serverless)

Comparison to AWS IAM:

  • AWS IAM uses Users + Groups + Roles + Policies
  • Azure uses Entra ID Users + Groups + Service Principals + RBAC
  • Managed identities = AWS IAM Roles (similar concept)
  • Conditional Access = AWS Organizations SCPs (broader)

Gataya Med

DevOps Engineer & Backend Developer. Sharing insights on cloud, automation, and scalable systems.

Comments (0)

Sarah Chen March 9, 2025

This is exactly what I needed! The initContainer approach solved our migration issues completely. Thanks for the detailed guide!

Reply

Leave a Comment