This PowerShell script automates the setup and validation of permissions for onboarding customers to the Crayon Azure Cost Control Service. It creates a service principal with the required read-only role assignments across Azure management, billing, reservations, and savings plans. The script supports Enterprise Agreement (EA), Microsoft Customer Agreement (MCA), and Cloud Solution Provider (CSP) environments.
- Version: 1.2.0 Authors: Tómas Harry Ottósson, Karol Kępka
- Version: 1.0.4 - 1.1.0 Authors: Karol Kępka
- Version: 1.0.3 (initial) Authors: Claus Sonderstrup, Suman Bhushal, Antti Mustonen
- Company: Crayon
The following PowerShell modules are required. The script will automatically install them if not present (falls back to CurrentUser scope if system-wide install fails):
- Az.Accounts - Azure authentication and account management
- Az.Reservations - Azure reservations management
- Az.BillingBenefits - Azure savings plans and billing benefits
- Az.Resources - Azure resource management and RBAC
- Az.Billing - Azure billing account management
- Microsoft.Graph.Authentication - Microsoft Graph authentication
- Microsoft.Graph.Applications - Microsoft Graph app registration management
- Microsoft.Graph.Identity.DirectoryManagement - Microsoft Graph directory operations
Note: On bastion hosts or air-gapped networks without internet access, modules must be pre-installed. The script detects this and provides guidance. Azure Cloud Shell is recommended as an alternative since all modules are pre-installed.
- PowerShell 5.1 or PowerShell 7.x (recommended)
- Windows PowerShell, PowerShell Core, or Azure Cloud Shell (recommended)
- Execution Policy set to allow script execution:
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
- Local file system access for creating directories and files:
- Windows:
C:\crayon - Linux/macOS:
~/crayon
- Windows:
- Administrative privileges may be required for module installation (script falls back to CurrentUser scope if unavailable)
- Valid Azure subscription with active billing account
- Appropriate agreement type: Enterprise Agreement (EA), Microsoft Customer Agreement (MCA), or Cloud Solution Provider (CSP)
- For EA customers: Enrollment must be onboarded to Azure portal for modern billing API access
- Global Administrator or Application Administrator role in Microsoft Entra ID
- Elevated access to Azure resources must be enabled for Global Administrators:
- Navigate to: Microsoft Entra ID → Properties → "Access management for Azure resources" → Yes
- Documentation: https://learn.microsoft.com/en-us/azure/role-based-access-control/elevate-access-global-admin?tabs=azure-portal
- Important: Remove elevated access immediately after running the script
- User Access Administrator role at the root management group level (granted automatically with elevated Global Admin access)
- Management Group Reader role (minimum) to access management group hierarchy
- Permission to register Azure resource providers (Microsoft.Management, Microsoft.Capacity, Microsoft.BillingBenefits)
For Enterprise Agreement (EA) customers:
- Enterprise Administrator role in the EA portal (ea.azure.com or billing.microsoft.com)
- EA enrollment must be onboarded to Azure portal for modern billing API access
For Microsoft Customer Agreement (MCA) customers:
- Billing Account Owner or Billing Profile Owner role
- Access to Cost Management + Billing in Azure portal
For Cloud Solution Provider (CSP) customers:
- Admin Agent role in Partner Center
- Access to customer's Azure subscriptions
The script requests the following Microsoft Graph scopes:
Application.ReadWrite.All- To create and manage app registrationsDirectory.Read.All- To read directory information
-
Run the script in Azure Cloud Shell (recommended) or a local PowerShell environment.
-
The script will ask about authentication method:
- Option 1: Interactive Browser (default)
- Option 2: Device Code (required for headless/bastion environments)
- Azure Cloud Shell uses existing authentication automatically.
-
The script will prompt for a specific tenant ID (optional — leave empty for default tenant).
-
Billing-first flow (new in v1.2.0): The script fetches and lists all billing accounts, then lets you select one. Agreement type is auto-detected from the selected account. Manual agreement type selection only appears as a fallback if billing accounts cannot be accessed (e.g. CSP tenants or missing billing permissions).
-
Pre-flight permission checks (new in v1.0.9): Before creating anything, the script validates all required permissions:
- Root management group access
- Role assignment permissions
- Microsoft Graph permissions
- Billing account access
- Checks for existing
CrayonCloudEconomicsReaderapp registration - Resource provider registration
If blocking issues are found, the script exits cleanly without creating any resources.
-
The script will then:
- Create an Azure AD Application and Service Principal (
CrayonCloudEconomicsReader) - Set expiration date for the service principal (default 36 months, customizable)
- Assign the following roles to the service principal:
- Reader (at management group root level)
- Cost Management Reader (at management group root level)
- Reservations Reader (at Microsoft.Capacity provider level)
- Savings Plan Reader (at Microsoft.BillingBenefits provider level)
- Carbon Optimization Reader (at management group root level)
- Reader role to individual savings plans (if any exist)
- Assign billing-specific roles based on agreement type:
- EA: Enrollment Reader role via Billing API
- MCA: Billing Account Reader role via Billing API
- Display a role assignment summary table showing success/failed/skipped status for each role
- Export two CSV files (see Output Files)
- Perform comprehensive validation tests using the created service principal
- Create an Azure AD Application and Service Principal (
The script generates two separate CSV files in the crayon directory:
| File | Contents | Sensitivity |
|---|---|---|
CrayonCloudEconomics-<TenantName>-<Date>.csv |
Tenant ID, tenant name, domain, country code, agreement type, App ID, secret expiry date | Safe to store |
CrayonCloudEconomics-<TenantName>-<Date>-SECRET.csv |
Tenant ID, App ID, client secret, secret expiry date | Sensitive — handle with care |
Important: The SECRET file contains the client secret credential. Send it separately and securely. Delete it after transfer.
Both files should be securely sent using https://deila.sensa.is to your Crayon representative. Remove the crayon directory after transfer.
- Script now fetches and lists billing accounts before asking for agreement type
- Agreement type is auto-detected from the selected billing account, eliminating manual selection and mismatch issues
- Manual agreement type prompt only appears as fallback when billing accounts cannot be accessed (e.g. CSP tenants or missing billing permissions)
- Replaced blind first-billing-account selection with interactive listing of all billing accounts
- Operator now sees every account (ID, display name, agreement type, status) and selects explicitly
- Deactivated account selection triggers a confirmation warning before proceeding
- Fixes onboarding failures on tenants with mixed EA/MCA billing accounts (e.g. post-migration tenants)
- Added pre-flight permission validation that checks all required permissions before creating anything — no more orphaned app registrations from permission failures
- Added agreement type auto-detection with mismatch warning (catches EA-to-MCA migrations)
- Wrapped all role assignments in try/catch with individual error handling
- Added role assignment summary table at the end of execution
- Clear error guidance for every failure mode
- Module install falls back to
CurrentUserscope if system-wide install fails - SPN propagation uses retry loop instead of fixed sleep
- Two separate CSV files: tenant info (safe) and secrets (sensitive) — previously a single file
- CSV always generates even if some role assignments fail
- Added login method selection (Interactive Browser / Device Code)
- Added tenant selection capability
- Aligned Microsoft Graph authentication with Azure authentication method
- Improved secure token handling using
NetworkCredentialwithAsSecureStringparameter - Added role assignments for BillingBenefits provider roles (read-only savings plan access)
- Improved support for Microsoft Customer Agreement (MCA) billing accounts, allowing the script to fetch billing account IDs for both EA and MCA agreements
- Fixed Service Principal propagation time issue by increasing wait time after creating the Service Principal
- Updated
Get-AzAccessTokencalls to use-AsSecureString:$falseto prepare for Az version 14.0.0 breaking changes
- Replaced deprecated AzureAD modules with Microsoft Graph PowerShell SDK
- Removed: AzureAD, AzureAD.Standard.Preview
- Added: Microsoft.Graph.Authentication, Microsoft.Graph.Applications, Microsoft.Graph.Identity.DirectoryManagement
- Updated all Azure AD operations to use Microsoft Graph API
- Changed connection method from
Connect-AzureADtoConnect-MgGraphwith appropriate scopes
Feel free to reach out to the authors or the Crayon FinOps Team for any assistance or feedback related to this script.