How to Grant IAM Roles for Azure Cost Management Data Export
Properly configuring Identity and Access Management (IAM) roles is critical for enabling Azure Cost Management data exports. Without the correct permissions, your exports will fail, and billing data won't reach your storage accounts or analytics platforms. This guide walks you through configuring RBAC (Role-Based Access Control) permissions for both human users and system-managed identities.
Overview
Azure Cost Management requires specific IAM roles to read billing data and write it to storage destinations. The most common roles needed are:
- Cost Management Reader: Grants read-only access to cost and billing data at subscription or billing scope
- Cost Management Contributor: Allows creation and management of cost exports
- Storage Blob Data Contributor: Enables writing export files to Azure Storage accounts
This guide covers three primary scenarios:
- Granting permissions to human users managing cost exports
- Assigning roles to system-managed identities for automated exports
- Configuring storage account permissions for export destinations
Prerequisites
Before you begin, ensure you have:
- Azure subscription with Owner or User Access Administrator role
- Azure CLI installed (version 2.30.0 or later) OR access to Azure Portal
- Azure PowerShell module (optional, for PowerShell method)
- Billing Account access if configuring exports at billing scope (EA, MCA, or MPA)
- Basic understanding of Azure RBAC concepts
Understanding Required Roles
Cost Management Roles
Cost Management Reader
- Scope: Subscription, Resource Group, Management Group, or Billing Account
- Purpose: Read cost data, view existing exports
- Use case: Analytics teams, finance personnel viewing costs
Cost Management Contributor
- Scope: Subscription, Resource Group, Management Group, or Billing Account
- Purpose: Create, modify, and delete cost exports
- Use case: Platform engineers configuring automated exports
Cost Management Data Reader (Billing scope only)
- Scope: Billing Account, Billing Profile
- Purpose: Read cost data across all subscriptions in EA or MCA
- Use case: Enterprise-wide cost reporting
Storage Roles
Storage Blob Data Contributor
- Scope: Storage Account or Container
- Purpose: Write cost export files to blob storage
- Required for: System-managed identity running the export
Method 1: Azure Portal (Interactive)
Step 1: Assign Cost Management Roles to Users
- Navigate to Azure Portal → Subscriptions
- Select the subscription you want to configure
- Click Access control (IAM) in the left navigation
- Click + Add → Add role assignment
- In the Role tab:
- Search for "Cost Management Contributor"
- Select the role
- Click Next
- In the Members tab:
- Select User, group, or service principal
- Click + Select members
- Search for the user or group by name
- Click Select
- Click Next
- In the Review + assign tab:
- Review the configuration
- Click Review + assign
Repeat this process for "Cost Management Reader" if you need read-only access.
Step 2: Configure Storage Account Permissions
- Navigate to your Storage Account where exports will be saved
- Click Access control (IAM)
- Click + Add → Add role assignment
- Search for and select Storage Blob Data Contributor
- In the Members tab:
- For automated exports: Select Managed identity
- Search for your export's system-managed identity (typically named after your export)
- For user access: Select User, group, or service principal and search for the user
- Click Review + assign
Step 3: Verify Role Assignments
- Go back to Access control (IAM)
- Click the Role assignments tab
- Filter by role name to verify assignments
- Look for your user or managed identity in the list
Method 2: Azure CLI (Command Line)
Step 1: Assign Cost Management Contributor Role
# Set variables
SUBSCRIPTION_ID="your-subscription-id"
USER_EMAIL="[email protected]"
# Get the user's object ID
USER_OBJECT_ID=$(az ad user show --id $USER_EMAIL --query id -o tsv)
# Assign Cost Management Contributor at subscription scope
az role assignment create \
--role "Cost Management Contributor" \
--assignee-object-id $USER_OBJECT_ID \
--assignee-principal-type User \
--scope "/subscriptions/$SUBSCRIPTION_ID"
# Verify assignment
az role assignment list \
--assignee $USER_OBJECT_ID \
--scope "/subscriptions/$SUBSCRIPTION_ID" \
--query "[?roleDefinitionName=='Cost Management Contributor']" \
--output table
Step 2: Assign Storage Permissions to Managed Identity
# Set variables
STORAGE_ACCOUNT_NAME="yourstorageaccount"
RESOURCE_GROUP="your-resource-group"
MANAGED_IDENTITY_NAME="your-export-identity"
# Get storage account resource ID
STORAGE_ID=$(az storage account show \
--name $STORAGE_ACCOUNT_NAME \
--resource-group $RESOURCE_GROUP \
--query id -o tsv)
# Get managed identity principal ID
MI_PRINCIPAL_ID=$(az identity show \
--name $MANAGED_IDENTITY_NAME \
--resource-group $RESOURCE_GROUP \
--query principalId -o tsv)
# Assign Storage Blob Data Contributor role
az role assignment create \
--role "Storage Blob Data Contributor" \
--assignee-object-id $MI_PRINCIPAL_ID \
--assignee-principal-type ServicePrincipal \
--scope $STORAGE_ID
# Verify assignment
az role assignment list \
--assignee $MI_PRINCIPAL_ID \
--scope $STORAGE_ID \
--output table
Step 3: Assign Billing Scope Permissions (Enterprise Agreement)
# For EA billing accounts
BILLING_ACCOUNT_ID="your-billing-account-id"
# Assign Cost Management Reader at billing scope
az role assignment create \
--role "Cost Management Reader" \
--assignee-object-id $USER_OBJECT_ID \
--assignee-principal-type User \
--scope "/providers/Microsoft.Billing/billingAccounts/$BILLING_ACCOUNT_ID"
Method 3: Azure PowerShell
Step 1: Install and Connect
# Install Azure PowerShell module (if not already installed)
Install-Module -Name Az -Repository PSGallery -Force -AllowClobber
# Connect to Azure
Connect-AzAccount
# Set subscription context
Set-AzContext -SubscriptionId "your-subscription-id"
Step 2: Assign Cost Management Roles
# Define variables
$subscriptionId = "your-subscription-id"
$userEmail = "[email protected]"
$scope = "/subscriptions/$subscriptionId"
# Get user object
$user = Get-AzADUser -UserPrincipalName $userEmail
# Assign Cost Management Contributor role
New-AzRoleAssignment `
-ObjectId $user.Id `
-RoleDefinitionName "Cost Management Contributor" `
-Scope $scope
# Assign Cost Management Reader role
New-AzRoleAssignment `
-ObjectId $user.Id `
-RoleDefinitionName "Cost Management Reader" `
-Scope $scope
# Verify assignments
Get-AzRoleAssignment `
-ObjectId $user.Id `
-Scope $scope | Format-Table DisplayName, RoleDefinitionName, Scope
Step 3: Configure Storage Access for Managed Identity
# Define variables
$resourceGroupName = "your-resource-group"
$storageAccountName = "yourstorageaccount"
$managedIdentityName = "your-export-identity"
# Get storage account
$storageAccount = Get-AzStorageAccount `
-ResourceGroupName $resourceGroupName `
-Name $storageAccountName
# Get managed identity
$managedIdentity = Get-AzUserAssignedIdentity `
-ResourceGroupName $resourceGroupName `
-Name $managedIdentityName
# Assign Storage Blob Data Contributor
New-AzRoleAssignment `
-ObjectId $managedIdentity.PrincipalId `
-RoleDefinitionName "Storage Blob Data Contributor" `
-Scope $storageAccount.Id
# Verify assignment
Get-AzRoleAssignment `
-ObjectId $managedIdentity.PrincipalId `
-Scope $storageAccount.Id | Format-Table
Method 4: Azure Resource Manager (ARM) Template
For infrastructure-as-code deployments, use ARM templates to assign roles:
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"managedIdentityPrincipalId": {
"type": "string"
},
"storageAccountName": {
"type": "string"
}
},
"variables": {
"storageBlobDataContributorRole": "[concat('/subscriptions/', subscription().subscriptionId, '/providers/Microsoft.Authorization/roleDefinitions/', 'ba92f5b4-2d11-453d-a403-e96b0029c9fe')]"
},
"resources": [
{
"type": "Microsoft.Storage/storageAccounts/providers/roleAssignments",
"apiVersion": "2022-04-01",
"name": "[concat(parameters('storageAccountName'), '/Microsoft.Authorization/', guid(uniqueString(parameters('storageAccountName'))))]",
"properties": {
"roleDefinitionId": "[variables('storageBlobDataContributorRole')]",
"principalId": "[parameters('managedIdentityPrincipalId')]",
"principalType": "ServicePrincipal"
}
}
]
}
Scope-Specific Configurations
Subscription Scope
Best for: Individual subscription cost tracking
# Subscription scope format
SCOPE="/subscriptions/{subscription-id}"
az role assignment create \
--role "Cost Management Contributor" \
--assignee-object-id $USER_OBJECT_ID \
--scope $SCOPE
Resource Group Scope
Best for: Project-specific cost tracking
# Resource group scope format
SCOPE="/subscriptions/{subscription-id}/resourceGroups/{resource-group-name}"
az role assignment create \
--role "Cost Management Reader" \
--assignee-object-id $USER_OBJECT_ID \
--scope $SCOPE
Management Group Scope
Best for: Multi-subscription governance
# Management group scope format
SCOPE="/providers/Microsoft.Management/managementGroups/{management-group-id}"
az role assignment create \
--role "Cost Management Reader" \
--assignee-object-id $USER_OBJECT_ID \
--scope $SCOPE
Billing Account Scope (EA)
Best for: Enterprise-wide cost visibility
# Billing account scope format
SCOPE="/providers/Microsoft.Billing/billingAccounts/{billing-account-id}"
az role assignment create \
--role "Cost Management Data Reader" \
--assignee-object-id $USER_OBJECT_ID \
--scope $SCOPE
Best Practices
1. Principle of Least Privilege
- Grant Reader roles for users who only need to view costs
- Reserve Contributor roles for platform administrators
- Use resource group scope when possible instead of subscription scope
2. Use Azure AD Groups
# Create a cost management group
GROUP_ID=$(az ad group create \
--display-name "Cost-Management-Analysts" \
--mail-nickname "cost-analysts" \
--query id -o tsv)
# Assign role to group instead of individuals
az role assignment create \
--role "Cost Management Reader" \
--assignee-object-id $GROUP_ID \
--assignee-principal-type Group \
--scope "/subscriptions/$SUBSCRIPTION_ID"
3. Document Role Assignments
Create a documentation table tracking role assignments:
User/Identity | Role | Scope | Purpose | Assigned Date |
---|---|---|---|---|
[email protected] | Cost Management Reader | Subscription | Monthly reporting | 2025-01-15 |
cost-export-mi | Storage Blob Data Contributor | Storage Account | Automated exports | 2025-01-15 |
4. Regularly Audit Permissions
# List all cost management role assignments
az role assignment list \
--all \
--query "[?contains(roleDefinitionName, 'Cost Management')]" \
--output table
# Export to CSV for review
az role assignment list \
--all \
--query "[?contains(roleDefinitionName, 'Cost Management')].[principalName,roleDefinitionName,scope]" \
--output tsv > cost-permissions-audit.csv
5. Enable Managed Identities
- Prefer system-managed identities for cost exports (automatically managed lifecycle)
- Use user-assigned identities when sharing across multiple exports
6. Separation of Duties
- Finance teams: Cost Management Reader only
- Platform teams: Cost Management Contributor + Storage access
- Automated exports: Managed identity with minimal required permissions
Troubleshooting
Issue: "Authorization failed" when creating export
Symptoms: Export creation fails with permission error
Resolution:
# Verify you have Contributor role
az role assignment list \
--assignee $(az ad signed-in-user show --query id -o tsv) \
--scope "/subscriptions/$SUBSCRIPTION_ID" \
--query "[?roleDefinitionName=='Cost Management Contributor']"
# If missing, request from subscription owner
Issue: Export runs but no data appears in storage
Symptoms: Export shows as successful but files not created
Resolution:
# Check managed identity has storage permissions
EXPORT_IDENTITY=$(az costmanagement export show \
--name "MyExport" \
--scope "/subscriptions/$SUBSCRIPTION_ID" \
--query identity.principalId -o tsv)
az role assignment list \
--assignee $EXPORT_IDENTITY \
--scope $STORAGE_ID \
--query "[?roleDefinitionName=='Storage Blob Data Contributor']"
# If missing, assign the role
az role assignment create \
--role "Storage Blob Data Contributor" \
--assignee-object-id $EXPORT_IDENTITY \
--assignee-principal-type ServicePrincipal \
--scope $STORAGE_ID
Issue: Cannot see billing account costs
Symptoms: Cost data shows $0 or missing subscriptions
Resolution:
# Verify billing scope permissions
BILLING_SCOPE="/providers/Microsoft.Billing/billingAccounts/{billing-account-id}"
az role assignment list \
--assignee $(az ad signed-in-user show --query id -o tsv) \
--scope $BILLING_SCOPE
# Request Billing Account Reader or Cost Management Data Reader from billing admin
Issue: Role assignment fails with "Principal not found"
Symptoms: Role assignment command returns principal does not exist error
Resolution:
# For users: ensure UPN is correct
az ad user show --id [email protected]
# For managed identities: ensure it exists
az identity show --name identity-name --resource-group rg-name
# For service principals: use application ID
az ad sp show --id {app-id}
Issue: Permissions work in Portal but not CLI
Symptoms: Can create exports in Portal but CLI commands fail
Resolution:
# Refresh CLI token
az account get-access-token --query accessToken -o tsv
# Clear and re-login
az logout
az login
# Verify correct subscription
az account show
az account set --subscription "subscription-name"
Verification Script
Use this PowerShell script to verify all required permissions are in place:
# Cost Management Permissions Verification Script
param(
[Parameter(Mandatory=$true)]
[string]$SubscriptionId,
[Parameter(Mandatory=$true)]
[string]$StorageAccountName,
[Parameter(Mandatory=$true)]
[string]$ResourceGroupName,
[Parameter(Mandatory=$false)]
[string]$ManagedIdentityName
)
Write-Host "Verifying Cost Management Permissions..." -ForegroundColor Cyan
# Set context
Set-AzContext -SubscriptionId $SubscriptionId | Out-Null
# Check current user permissions
$currentUser = Get-AzContext
Write-Host "`nCurrent User: $($currentUser.Account.Id)" -ForegroundColor Yellow
$userRoles = Get-AzRoleAssignment `
-SignInName $currentUser.Account.Id `
-Scope "/subscriptions/$SubscriptionId" `
| Where-Object { $_.RoleDefinitionName -like "*Cost Management*" }
if ($userRoles) {
Write-Host "✓ User has Cost Management roles:" -ForegroundColor Green
$userRoles | ForEach-Object { Write-Host " - $($_.RoleDefinitionName)" }
} else {
Write-Host "✗ User missing Cost Management roles" -ForegroundColor Red
}
# Check storage account permissions
$storageAccount = Get-AzStorageAccount `
-ResourceGroupName $ResourceGroupName `
-Name $StorageAccountName
if ($ManagedIdentityName) {
$identity = Get-AzUserAssignedIdentity `
-ResourceGroupName $ResourceGroupName `
-Name $ManagedIdentityName
$storageRoles = Get-AzRoleAssignment `
-ObjectId $identity.PrincipalId `
-Scope $storageAccount.Id `
| Where-Object { $_.RoleDefinitionName -like "*Storage Blob Data*" }
if ($storageRoles) {
Write-Host "✓ Managed Identity has storage permissions:" -ForegroundColor Green
$storageRoles | ForEach-Object { Write-Host " - $($_.RoleDefinitionName)" }
} else {
Write-Host "✗ Managed Identity missing storage permissions" -ForegroundColor Red
}
}
Write-Host "`nVerification complete!" -ForegroundColor Cyan
Run the verification script:
.\Verify-CostPermissions.ps1 `
-SubscriptionId "your-sub-id" `
-StorageAccountName "yourstorage" `
-ResourceGroupName "your-rg" `
-ManagedIdentityName "cost-export-identity"
Next Steps
Once permissions are configured:
- Create your first export: Follow the "How to Set Up Azure Cost Management Data Export" guide
- Monitor export health: See "How to Monitor Cost Export Status and Data Freshness in Azure"
- Secure your data: Review "How to Secure Cost Management Data in Azure Storage and Synapse"
- Set up alerts: Configure budget alerts with "How to Set Up Cost Alerts and Budgets in Azure"
Related Resources
Need Professional Help?
Our team of experts can help you implement and configure these solutions for your organization.