Azure Prerequisites
This guide provides step-by-step instructions for configuring all Azure prerequisites
required by the
The following prerequisites must be completed before using the <no value>_tenancy_azure resource:
- Create a Service Principal
- Grant Microsoft Graph API Permissions
- Assign Microsoft Entra ID Roles (optional)
- Configure Azure RBAC Permissions
- Configure Billing Account Permissions
1. Create a Service Principal
The credentials block of the <no value>_tenancy_azure resource.
Using the Azure Portal
- Navigate to Microsoft Entra ID > App registrations > New registration
- Set the following values:
- Name: a descriptive name (e.g.
app-volocloud-platform-prod) - Supported Account Types: "Accounts in this organizational directory only (single-tenant)"
- Redirect URI: leave blank
- Name: a descriptive name (e.g.
- Click Register
- Note the Application (client) ID and Directory (tenant) ID from the overview page
Generate a Client Secret
- In the App Registration, navigate to Certificates & secrets > Client secrets
- Click New client secret
- Enter a description and select an expiry period
- Click Add
- Copy the Value immediately — it will not be shown again
Warning
Store the client secret securely. If lost, you will need to generate a new one.
Using the Azure CLI
az ad app create --display-name "app-volocloud-platform-prod"
Note the appId from the output, then create the Service Principal:
az ad sp create --id <appId>
Create a client secret:
az ad app credential reset --id <appId> --append
The output provides:
| Output Field | Volocloud Credential |
|---|---|
appId |
client_id |
password |
client_secret |
tenant |
tenant_id |
2. Grant Microsoft Graph API Permissions
The
Required Permissions
| Permission | Type | Purpose |
|---|---|---|
Application.ReadWrite.OwnedBy |
Application | Manage app registrations that this app creates or owns |
Directory.Read.All |
Application | Read directory data |
Group.ReadWrite.All |
Application | Manage security groups |
Policy.Read.All |
Application | Read policies |
Policy.ReadWrite.ApplicationConfiguration |
Application | Manage application configuration policies |
User.Read |
Delegated | Sign in and read user profile |
Using the Azure Portal
- Navigate to Microsoft Entra ID > App registrations > select your app
- Go to API permissions > Add a permission
- Select Microsoft Graph > Application permissions
- Add each permission listed above
- Click Grant admin consent for [your tenant]
Note
Admin consent MUST be granted by a Global Administrator or Privileged Role Administrator.
Using the Azure CLI
# Get the Microsoft Graph Service Principal object ID
GRAPH_SP_ID=$(az ad sp list --filter "appId eq '00000003-0000-0000-c000-000000000000'" \
--query "[0].id" -o tsv)
# Get your app's Service Principal object ID
APP_SP_ID=$(az ad sp list --filter "appId eq '<your-app-client-id>'" \
--query "[0].id" -o tsv)
# Required Application permission IDs (Microsoft Graph)
# Application.ReadWrite.OwnedBy: 18a4783c-866b-4cc7-a460-3d5e5662c884
# Directory.Read.All: 7ab1d382-f21e-4acd-a863-ba3e13f7da61
# Group.ReadWrite.All: 62a82d76-70ea-41e2-9197-370581804d09
# Policy.Read.All: 246dd0d5-5bd0-4def-940b-0421030a5b68
# Policy.ReadWrite.ApplicationConfiguration: be74164b-cff1-491c-8741-e671cb536e13
PERMISSIONS=(
"18a4783c-866b-4cc7-a460-3d5e5662c884"
"7ab1d382-f21e-4acd-a863-ba3e13f7da61"
"62a82d76-70ea-41e2-9197-370581804d09"
"246dd0d5-5bd0-4def-940b-0421030a5b68"
"be74164b-cff1-491c-8741-e671cb536e13"
)
for PERM_ID in "${PERMISSIONS[@]}"; do
az rest --method post \
--url "https://graph.microsoft.com/v1.0/servicePrincipals/${APP_SP_ID}/appRoleAssignments" \
--body "{
\"principalId\": \"${APP_SP_ID}\",
\"resourceId\": \"${GRAPH_SP_ID}\",
\"appRoleId\": \"${PERM_ID}\"
}"
done
# Grant delegated User.Read permission (admin consent)
# This is done via the Azure Portal: API permissions > Add > Microsoft Graph > Delegated > User.Read > Grant admin consent
3. Assign Microsoft Entra ID Roles (Optional)
If using Microsoft Entra Domain Services, the Service Principal requires the following Entra ID built-in directory roles:
| Role | Purpose |
|---|---|
| Application Administrator | Manage app registrations and enterprise apps |
| Groups Administrator | Manage group creation and membership |
Using the Azure Portal
- Navigate to Microsoft Entra ID > Roles and administrators
- Search for Application Administrator, click on it
- Click Add assignments > search for your Service Principal > Add
- Repeat for Groups Administrator
Using the Azure CLI
APP_SP_ID=$(az ad sp list --filter "appId eq '<your-app-client-id>'" \
--query "[0].id" -o tsv)
# Application Administrator role template ID
az rest --method post \
--url "https://graph.microsoft.com/v1.0/roleManagement/directory/roleAssignments" \
--body "{
\"principalId\": \"${APP_SP_ID}\",
\"roleDefinitionId\": \"9b895d92-2cd3-44c7-9d02-a6ac2d5ea5c3\",
\"directoryScopeId\": \"/\"
}"
# Groups Administrator role template ID
az rest --method post \
--url "https://graph.microsoft.com/v1.0/roleManagement/directory/roleAssignments" \
--body "{
\"principalId\": \"${APP_SP_ID}\",
\"roleDefinitionId\": \"fdd7a751-b60b-444a-984c-02652fe8fa1c\",
\"directoryScopeId\": \"/\"
}"
Note
These roles are only required if microsoft_entra_domain_services is enabled in the
tenancy configuration. If not using Entra Domain Services, this step can be skipped.
4. Configure Azure RBAC Permissions
Note
This step is only required for MPA (CSP) billing accounts where subscriptions are
pre-provisioned by the CSP partner. For EA and MCA billing accounts, the Service Principal
creates subscriptions via the billing API and automatically becomes Owner of those subscriptions.
The
For MPA billing accounts, the Service Principal requires Owner role on each subscription provisioned by the CSP partner.
Using the Azure Portal
- Navigate to Subscriptions in the Azure Portal
- Select the subscription
- Click Access control (IAM) > Add > Add role assignment
- Select Owner role
- Under Members, search for and select your Service Principal
- Click Review + assign
- Repeat for each subscription provisioned by the CSP partner
Using the Azure CLI
# Assign Owner on each subscription
az role assignment create \
--assignee "<service-principal-app-id>" \
--role "Owner" \
--scope "/subscriptions/<subscription-id>"
Management Group Creation Restrictions
By default, any principal in an Azure tenant can create Management Groups under the Tenant Root Group. However, some organizations enable the "Require permissions to create new management groups" setting, which restricts Management Group creation to principals with explicit write access on the Tenant Root Group.
If this setting is enabled in your tenant, the Service Principal requires elevated access to create the Management Group hierarchy. A Global Administrator must first elevate their access and then grant the Service Principal the required permissions:
- In the Azure Portal, navigate to Microsoft Entra ID > Properties
- Under Access management for Azure resources, set the toggle to Yes
- Click Save
- Assign the Service Principal a role with
Microsoft.Management/managementGroups/writepermission at the Tenant Root Group scope:
# Grant Management Group Contributor on the Tenant Root Group
az role assignment create \
--assignee "<service-principal-app-id>" \
--role "Management Group Contributor" \
--scope "/providers/Microsoft.Management/managementGroups/<tenant-root-group-id>"
Note
The Tenant Root Group ID is the same as your Azure AD Tenant ID. The Global Administrator can disable the elevated access toggle after granting the role assignment.
5. Configure Billing Account Permissions
The
Enterprise Agreement (EA)
For EA billing accounts, the Service Principal requires two billing roles:
| Role | Scope | Purpose |
|---|---|---|
| Enrollment account subscription creator | Enrollment Account | Create subscriptions |
| Enrollment reader | Enrollment (billing account) | View cost data, manage budgets at Management Group scope |
Note
These roles are not standard Azure RBAC roles. They are EA billing-specific roles that must be assigned via the REST API. They are not visible in the Azure Portal.
Step 1: Create a Department (Azure Portal)
- Sign in to the Azure Portal as an Enterprise Administrator
- Navigate to Cost Management + Billing > select your EA billing account
- Go to Departments > Add
- Enter a department name (e.g.
Cloud Platform) - Optionally set a department administrator
- Click Save
Step 2: Create an Enrollment Account (Azure Portal)
- In the EA billing account, navigate to Accounts > Add
- Set the following:
- Account name: a descriptive name (e.g.
Volo Cloud Foundations) - Account owner email: a service account email or shared mailbox
- Department: select the department created above
- Dev/Test: enable dev/test subscriptions to allow creation of Dev/Test subscriptions
- Account name: a descriptive name (e.g.
- Click Add
Note
The Account Owner email will receive an activation email. The account must be activated before subscriptions can be created under it. Once activated, note down the Enrollment Account ID (visible in the account details).
Step 3: Identify Your Billing IDs
You will need:
- Billing Account ID (Enrollment ID): visible in Cost Management + Billing > Properties
- Enrollment Account ID: visible under Accounts in the EA billing account
Alternatively, use the Azure CLI:
# Get the billing account (enrollment) ID
az billing account list \
--query "[?agreementType=='EnterpriseAgreement'].{name:name, displayName:displayName}" \
-o table
# Get enrollment accounts
az rest --method get \
--url "https://management.azure.com/providers/Microsoft.Billing/billingAccounts/<billing-account-id>/enrollmentAccounts?api-version=2024-04-01" \
--query "value[].{name:name, displayName:properties.displayName, owner:properties.accountOwner}" \
-o table
Step 4: Assign Subscription Creator Role to the Service Principal
This must be done by the Enrollment Account Owner (or an EA Administrator) using the REST API.
First, get the Service Principal's Object ID (not the App ID):
az ad sp list --display-name "<service-principal-name>" \
--query "[].{displayName:displayName, objectId:id}" -o table
Generate a unique GUID for the role assignment:
# macOS/Linux
python3 -c "import uuid; print(uuid.uuid4())"
# PowerShell
[guid]::NewGuid()
Assign the Subscription Creator role on the Enrollment Account:
az rest --method put \
--url "https://management.azure.com/providers/Microsoft.Billing/billingAccounts/<billing-account-id>/enrollmentAccounts/<enrollment-account-id>/billingRoleAssignments/<new-guid>?api-version=2019-10-01-preview" \
--body '{
"properties": {
"principalId": "<service-principal-object-id>",
"principalTenantId": "<tenant-id>",
"roleDefinitionId": "/providers/Microsoft.Billing/billingAccounts/<billing-account-id>/enrollmentAccounts/<enrollment-account-id>/billingRoleDefinitions/a0bcee42-bf30-4d1b-926a-48d21664ef71"
}
}'
A 200 OK response confirms the role was assigned successfully.
Step 5: Assign Enrollment Reader Role to the Service Principal
This role is required for the Service Principal to manage budgets at the Management Group scope. Without it, budget operations at MG-level will silently fail on EA billing accounts.
This must be done by a user with the Enrollment Administrator (enrollment writer) role.
Generate a new unique GUID and assign the Enrollment Reader role at the Enrollment level:
az rest --method put \
--url "https://management.azure.com/providers/Microsoft.Billing/billingAccounts/<billing-account-id>/billingRoleAssignments/<new-guid>?api-version=2019-10-01-preview" \
--body '{
"properties": {
"principalId": "<service-principal-object-id>",
"principalTenantId": "<tenant-id>",
"roleDefinitionId": "/providers/Microsoft.Billing/billingAccounts/<billing-account-id>/billingRoleDefinitions/24f8edb6-1668-4659-b5e2-40bb5f3a7d7e"
}
}'
A 200 OK response confirms the role was assigned successfully.
Verify EA Role Assignments
To verify the roles were assigned correctly:
# Check enrollment-level roles
az rest --method get \
--url "https://management.azure.com/providers/Microsoft.Billing/billingAccounts/<billing-account-id>/billingRoleAssignments?api-version=2024-04-01" \
-o json
# Check enrollment account-level roles
az rest --method get \
--url "https://management.azure.com/providers/Microsoft.Billing/billingAccounts/<billing-account-id>/enrollmentAccounts/<enrollment-account-id>/billingRoleAssignments?api-version=2024-04-01" \
-o json
EA Billing Role Assignments
- Each role assignment requires a unique GUID as the assignment name
- The
principalIdmust be the Service Principal's Object ID from Enterprise Applications, NOT the Application (client) ID from App Registrations - These role assignments are not visible in the Azure Portal — they can only be managed via REST API
- A Service Principal can have only one role per scope — assigning a new role at the same scope replaces the previous one
EA Configuration Reference
Once the billing permissions are configured, use the following values in your
<no value>_tenancy_azure resource:
billing = {
account_type = "ea"
ea = {
account_id = "<billing-account-id>" # The EA enrollment number
enrollment_id = "<enrollment-account-id>" # The enrollment account under the EA
}
}
Microsoft Customer Agreement (MCA)
For MCA billing accounts, the Service Principal requires the Billing profile contributor role on a dedicated Billing Profile. This allows the provider to create subscriptions across all invoice sections under the profile and supports future management of invoice sections.
Step 1: Identify Your Billing IDs
You will need:
- Billing Account ID: visible in Cost Management + Billing > Properties
- Billing Profile ID: visible under Billing profiles
- Invoice Section ID: visible under the billing profile's Invoice sections
Using the Azure CLI:
# List MCA billing accounts
az billing account list \
--query "[?agreementType=='MicrosoftCustomerAgreement'].{name:name, displayName:displayName}" \
-o table
# List billing profiles
az rest --method get \
--url "https://management.azure.com/providers/Microsoft.Billing/billingAccounts/<billing-account-id>/billingProfiles?api-version=2024-04-01" \
--query "value[].{name:name, displayName:properties.displayName}" \
-o table
# List invoice sections
az rest --method get \
--url "https://management.azure.com/providers/Microsoft.Billing/billingAccounts/<billing-account-id>/billingProfiles/<billing-profile-id>/invoiceSections?api-version=2024-04-01" \
--query "value[].{name:name, displayName:properties.displayName}" \
-o table
Step 2: Assign Billing Permissions
Grant the Service Principal Billing profile contributor on the Billing Profile:
- Navigate to Cost Management + Billing > select your MCA billing account
- Go to Billing profiles > select the billing profile
- Click Access control (IAM) > Add
- Select Billing profile contributor role
- Search for and select your Service Principal
- Click Save
Alternatively, use the Azure CLI:
az rest --method put \
--url "https://management.azure.com/providers/Microsoft.Billing/billingAccounts/<billing-account-id>/billingProfiles/<billing-profile-id>/billingRoleAssignments/<new-guid>?api-version=2024-04-01" \
--body '{
"properties": {
"principalId": "<service-principal-object-id>",
"principalTenantId": "<tenant-id>",
"roleDefinitionId": "/providers/Microsoft.Billing/billingAccounts/<billing-account-id>/billingProfiles/<billing-profile-id>/billingRoleDefinitions/40000000-aaaa-bbbb-cccc-100000000001"
}
}'
Note
For MCA billing accounts, budget operations at both subscription and Management Group scope work with standard Azure RBAC (the Service Principal automatically becomes Owner of subscriptions it creates) without additional billing roles.
MCA Configuration Reference
Once the billing permissions are configured, use the following values in your
<no value>_tenancy_azure resource:
billing = {
account_type = "mca"
mca = {
account_id = "<billing-account-id>"
profile_id = "<billing-profile-id>"
invoice_id = "<invoice-section-id>"
}
}
Microsoft Partner Agreement (MPA)
For MPA (CSP) billing accounts, subscriptions are managed by the Cloud Solution Provider partner. The CSP partner must grant the Service Principal appropriate permissions on the subscriptions they provision.
MPA Billing Limitation
MPA (CSP) billing accounts do not support Management Group-level budgets. Budgets can
only be created at the individual subscription scope. The budgets.root configuration
block is not supported for MPA billing accounts.
Prerequisites
- The CSP partner provisions the core subscriptions (connectivity, identity, management, security)
- The CSP partner grants the Service Principal Owner role on each provisioned subscription
- The Service Principal must also have Owner at the root Management Group level (as described in Configure Azure RBAC Permissions)
Step 1: CSP Partner Grants Subscription Access
The CSP partner must assign Owner on each subscription to the Service Principal. This is done by the partner through their Partner Center or via the Azure Portal/CLI on the customer's tenant:
# For each subscription provisioned by the CSP
az role assignment create \
--assignee "<service-principal-app-id>" \
--role "Owner" \
--scope "/subscriptions/<subscription-id>"
Note
The CSP partner must perform this step, or delegate Admin on Behalf Of (AOBO) access so that an administrator in the customer tenant can assign the role.
Step 2: Provide Existing Subscription IDs
Since MPA billing accounts use pre-provisioned subscriptions, you must provide the existing subscription IDs in the configuration rather than having the provider create them.
MPA Configuration Reference
Use the following values in your <no value>_tenancy_azure resource:
billing = {
account_type = "mpa"
existing = {
connectivity_subscription_id = "<connectivity-subscription-id>"
identity_subscription_id = "<identity-subscription-id>"
management_subscription_id = "<management-subscription-id>"
security_subscription_id = "<security-subscription-id>"
}
}
Budgets for MPA billing accounts are only supported at the subscription level:
subscriptions = {
connectivity = {
abbreviation = "conn"
budgets = [
{
amount = 2000
notifications = [
{
contact_emails = ["alerts@example.com"]
threshold = 90
}
]
}
]
# ... other configuration ...
}
}
Summary of Required Permissions
| Component | Permission | Scope | Required |
|---|---|---|---|
| Azure RBAC | Owner | Each provisioned subscription | If billing type is MPA |
| Microsoft Graph API | Application.ReadWrite.OwnedBy | Tenant | Yes |
| Microsoft Graph API | Directory.Read.All | Tenant | Yes |
| Microsoft Graph API | Group.ReadWrite.All | Tenant | Yes |
| Microsoft Graph API | Policy.Read.All | Tenant | Yes |
| Microsoft Graph API | Policy.ReadWrite.ApplicationConfiguration | Tenant | Yes |
| Microsoft Graph API | User.Read (Delegated) | Tenant | Yes |
| Entra ID Role | Application Administrator | Tenant | If using Entra Domain Services |
| Entra ID Role | Groups Administrator | Tenant | If using Entra Domain Services |
| EA Billing | Subscription Creator | Enrollment Account | If billing type is EA |
| EA Billing | Enrollment Reader | Enrollment (billing account) | If billing type is EA |
| MCA Billing | Billing profile contributor | Billing Profile | If billing type is MCA |
| MPA Billing | Owner | Each provisioned subscription | If billing type is MPA |
Configuring the Provider
Once all prerequisites are in place, configure the <no value>_tenancy_azure resource credentials:
resource "<no value>_tenancy_azure" "example" {
# ... configuration ...
credentials = {
client_id = "<application-client-id>"
client_secret = "<client-secret-value>"
subscription_id = "<management-subscription-id>"
tenant_id = "<directory-tenant-id>"
}
}
Warning
Do not hard-code credentials in your Terraform configuration. Use Terraform variables, environment variables, or a secrets manager to provide sensitive values.
The subscription_id should reference an existing subscription where the Service Principal
has Owner access. This subscription is used as the default context for Azure Resource Manager
operations.
Troubleshooting
EA Budgets Not Working
If budget creation succeeds but the budget does not appear in the Azure Portal or subsequent reads return 404, the Service Principal is likely missing the Enrollment Reader role at the enrollment level. The Azure Consumption API may accept the request but fail to persist the budget without this billing role.
Fix: Assign the Enrollment Reader role as described in Step 5.
Service Principal Object ID vs Application ID
A common error when assigning EA billing roles is using the Application (client) ID instead of the Service Principal Object ID. These are different values:
- Application (client) ID: found in App Registrations — used for authentication
- Service Principal Object ID: found in Enterprise Applications — used for role assignments
To find the correct Object ID:
az ad sp list --display-name "<app-name>" --query "[].{name:displayName, objectId:id}" -o table
Admin Consent Not Granted
If the provider fails with Graph API permission errors, verify that admin consent was granted for all required permissions. Navigate to the App Registration > API permissions and check that each permission shows a green checkmark under "Status".