Onboarding
Below is necessary information that is needed for application setup. Activities, scripts below will help you in obtaining them.
Subscription Id: "xxxxx"
Management Group Id: "xxxxx"
Management Group Name: "xxxxx"
Application Client Id: "xxxxx"
Application Client Secret: "xxxxx"
Secret Expires On: "xxxxx"
TenantId: "xxxxx"
You can decide where to store the output of analysis, it can be in your or our tenant. Generally, for POC / Test it is okay to use our storage account, but later storing data in your tenant is the preferred option as it means that you own your data.
Optionally, output details
Manually
1 - Creating Service Principal
Go to Microsoft Entra ID in the Azure portal and choose App registrations then click New registration.
Use name according to your naming conventions for example sp-bkb-cloud
.
For other settings keep defaults as is in screenshot and click Register.
Note down following details:
- Application (client) ID
- Directory (tenant) ID
Go to Certificates & secrets
and generate new secret, set validity one year or higher.
Note down the generated secret and expiry date.
2 - Granting Permissions
Scope of permissions can be either Subscription
or Management Group
.
Process is additive, meaning that if you start with Subscription, you can later go for Management Group.
If you want to use multiple subscriptions, just repeat same process for those.
For scanning whole tenant, we need permissions on the top Management Group, if you are using custom Management Group that has all desired subscriptions, it is fine to grant permissions only for that Management Group, in other cases target Management group is Tenant Root Group
.
Creating custom role
Open the container resource e.g. Management Group.
Open Access control (IAM)
and choose Add
followed by Add custom role
, to trigger wizard for creating new role.
Specify name of custom role such as BKB Cloud
and optionally description.
Without changing anything else, go to JSON
tab.
Copy below JSON and paste it into working text area.
Change assignableScopes
to array of resource identifiers, based on your scope.
Example has resource identifier for both Subscriptions and Management Groups.
If you intend to grant permissions on only one Subscription, delete the other line from Management Group and vice versa.
Finish the wizard for creating custom role.
{
"properties": {
"roleName": "BKB Cloud",
"description": "View all resources, modify tags and does not allow to make any other changes.",
"assignableScopes": [
"/providers/Microsoft.Management/managementGroups/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
],
"permissions": [
{
"actions": [
"*/read",
"Microsoft.Resources/tags/*"
],
"notActions": [],
"dataActions": [],
"notDataActions": []
}
]
}
}
Assigning custom role to service principal
To assign created custom role to Service Principal, on the same resource as you wish to grant access, navigate to Access control (IAM)
blade and choose Add
followed by Add role assignment
Search for custom role, select it and choose next.
Choose User, group, or service principal
option and Select members
, in the Select members
window search for service principal by name.
Finish the assignment.
If you followed this guide sequentially, at this point you have:
- Service Principal with its Application ID, Client Secret and its expiration time.
- Tenant Id, also called Directory ID
- Scope such as Subscription ID or Management Group Name
- Permissions to the Service Principal are granted on the desired scope
3 - (Optional) Creating output storage account
Optionally create storage account to store the output, script.
4 - Granting access to cost data
For granting access to cost data, use the PowerShell script.
Via PowerShell Script
Authenticate to Azure before running the scripts by executing Connect-AzAccount
in your terminal.
Before you execute any of the PowerShell scripts, please make sure following preconditions are met:
- Script requires PowerShell 7.4.0+, Azure PowerShell module
- User executing script needs to have permissions to:
- Create App Registrations (Application Administrator)
- Create custom roles at desired level e.g. Subscription, Management Group (Owner level permissions)
- Assign permissions at desired level e.g. Owner of Subscription
1 - Creating Service Principal
Script will perform manual steps above and write info to console.
Copy the script into a text editor, change the initial values if needed and run the edited script in authenticated PowerShell terminal.
#region Initial Values
[string] $ServicePrincipalName = 'sp-bkb-cloud'; # change according to your naming conventions
[int] $SecretValidityInYears = 2;
#endregion
$ErrorActionPreference = 'Stop';
$servicePrincipal = Get-AzADServicePrincipal -DisplayName $ServicePrincipalName;
if ($ServicePrincipal -eq $null) {
Write-Host "Creating Service Principal $ServicePrincipalName" -ForegroundColor Yellow;
$servicePrincipal = New-AzADServicePrincipal -DisplayName $ServicePrincipalName;
Start-Sleep -Seconds 10;
}
$appObj = Get-AzADApplication -ApplicationId $servicePrincipal.AppId;
$currentDateTime = Get-Date;
$endDate = $currentDateTime.AddYears($SecretValidityInYears);
Write-Host "Creating Service Principal Secret";
$newCredential = New-AzADAppCredential -ObjectId $appObj.Id -StartDate $currentDateTime -EndDate $endDate;
$result = [PSCustomObject]@{
TenantId = (Get-AzContext).Tenant.Id;
ClientId = $servicePrincipal.AppId;
ClientSecret = $newCredential.SecretText;
SecretExpiresOn = $endDate.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss");
}
Write-Host "";
Write-Host "";
Write-Host "Please share below JSON:";
Write-Host ($result | ConvertTo-Json) -ForegroundColor Green;
2 - Granting Subscription Level Permissions
Use below script to grant subscription level permissions including creation of custom role.
Script will perform manual steps above and write info to console.
Copy the script into a text editor, change the initial values if needed and run the edited script in authenticated PowerShell terminal.
#region Initial Values
[string] $CustomRoleName = 'BKB Cloud';
[string] $ClientId = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'; # Service Principal (app registration) Application ID
[string] $SubscriptionId = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx';
#endregion
$ErrorActionPreference = 'Stop';
[string[]] $CustomRoleActions = @(
"*/read",
"Microsoft.Resources/tags/*"
);
$RolesToAssign = @(
$CustomRoleName,
'Load Test Reader'
);
$customRole = [Microsoft.Azure.Commands.Resources.Models.Authorization.PSRoleDefinition]::new();
$customRole.Name = $CustomRoleName;
$customRole.Description = 'View all resources, modify tags and does not allow to make any other changes.';
$customRole.Actions = $CustomRoleActions;
$customRole.IsCustom = $true;
$scope = '/subscriptions/' + $SubscriptionId;
[Void](Set-AzContext -Subscription $SubscriptionId);
$existingRole = Get-AzRoleDefinition -Name $CustomRoleName -WarningAction SilentlyContinue;
if ($existingRole -eq $null) {
$customRole.AssignableScopes = @($scope);
Write-Host "Creating Custom Role `"$CustomRoleName`"" -ForegroundColor Yellow;
[Void](New-AzRoleDefinition -Role $customRole);
Write-Host "Waiting for Role to be available via API, this might take a while..." -ForegroundColor Yellow;
do{
Start-Sleep -Seconds 10;
}until(Get-AzRoleDefinition -Name $CustomRoleName -WarningAction SilentlyContinue);
$customRole.Id = (Get-AzRoleDefinition -Name $CustomRoleName).Id;
}else {
$customRole.Id = $existingRole.Id;
$customRole.AssignableScopes = (@($scope) + $existingRole.AssignableScopes) | Select-Object -Unique;
Write-Host "Updating Custom Role `"$CustomRoleName`"" -ForegroundColor Yellow;
[Void](Set-AzRoleDefinition -Role $customRole);
}
$servicePrincipal = Get-AzADServicePrincipal -ApplicationId $ClientId;
foreach ($role in $RolesToAssign) {
$roleAssignment = Get-AzRoleAssignment -ObjectId $servicePrincipal.Id -RoleDefinitionName $role -Scope $scope;
if ($roleAssignment -eq $null) {
Write-Host "Assigning '$($role)' role to Service Principal";
[Void](New-AzRoleAssignment -ObjectId $servicePrincipal.Id -RoleDefinitionName $role -Scope $scope);
}
}
$result = [PSCustomObject]@{
SubscriptionId = $SubscriptionId;
}
Write-Host "";
Write-Host "Please share below JSON:";
Write-Host ($result | ConvertTo-Json) -ForegroundColor Green;
2 - Granting Management Group Level Permissions
Use below script to grant management group level permissions including creation of custom role.
Script will perform manual steps above and write info to console.
Copy the script into a text editor, change the initial values if needed and run the edited script in authenticated PowerShell terminal.
#region Initial Values
[string] $CustomRoleName = 'BKB Cloud';
[string] $ClientId = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'; # Service Principal (app registration) Application ID
[string] $ManagementGroupDisplayName = 'xxxxx';
#endregion
$ErrorActionPreference = 'Stop';
[string[]] $CustomRoleActions = @(
"*/read",
"Microsoft.Resources/tags/*"
);
$RolesToAssign = @(
$CustomRoleName,
'Load Test Reader'
);
$customRole = [Microsoft.Azure.Commands.Resources.Models.Authorization.PSRoleDefinition]::new();
$customRole.Name = $CustomRoleName;
$customRole.Description = 'View all resources, modify tags and does not allow to make any other changes.';
$customRole.Actions = $CustomRoleActions;
$customRole.IsCustom = $true;
$mgmtGroup = Get-AzManagementGroup | Where-Object {$_.DisplayName -eq $ManagementGroupDisplayName -or $_.Name -eq $ManagementGroupDisplayName};
$scope = $mgmtGroup.Id;
$existingRole = Get-AzRoleDefinition -Name $CustomRoleName -WarningAction SilentlyContinue;
if ($existingRole -eq $null) {
$customRole.AssignableScopes = @($scope);
Write-Host "Creating Custom Role `"$CustomRoleName`"" -ForegroundColor Yellow;
[Void](New-AzRoleDefinition -Role $customRole);
Write-Host "Waiting for Role to be available via API, this might take a while..." -ForegroundColor Yellow;
do{
Start-Sleep -Seconds 10;
}until(Get-AzRoleDefinition -Name $CustomRoleName -WarningAction SilentlyContinue);
$customRole.Id = (Get-AzRoleDefinition -Name $CustomRoleName).Id;
}else {
$customRole.Id = $existingRole.Id;
$customRole.AssignableScopes = (@($scope) + $existingRole.AssignableScopes) | Select-Object -Unique;
Write-Host "Updating Custom Role `"$CustomRoleName`"" -ForegroundColor Yellow;
[Void](Set-AzRoleDefinition -Role $customRole);
}
$servicePrincipal = Get-AzADServicePrincipal -ApplicationId $ClientId;
foreach ($role in $RolesToAssign) {
$roleAssignment = Get-AzRoleAssignment -ObjectId $servicePrincipal.Id -RoleDefinitionName $role -Scope $scope;
if ($roleAssignment -eq $null) {
Write-Host "Assigning '$($role)' role to Service Principal";
[Void](New-AzRoleAssignment -ObjectId $servicePrincipal.Id -RoleDefinitionName $role -Scope $scope);
}
}
$result = [PSCustomObject]@{
ManagementGroupDisplayName = $mgmtGroup.DisplayName;
ManagementGroupName = $mgmtGroup.Name;
}
Write-Host "";
Write-Host "Please share below JSON:";
Write-Host ($result | ConvertTo-Json) -ForegroundColor Green;
3 - (Optional) Creating output storage account via PowerShell
Script creates resource group and storage account if it doesn't already exist.
#region Parameters
[string] $Subscription = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'; # name or id where storage account is located
[string] $ResourceGroupName = 'rg-bkbcloud-output'; # name of the resource group where storage account is located
[string] $StorageAccountName = 'sabkbcloudxxx'; # name of the storage account, if it doesnt exist it will be created
[string] $ClientId = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'; # Service Principal (app registration) Application ID
[string] $Location = 'westeurope'; # location of the storage account and resource group
#endregion
$ErrorActionPreference = 'Stop';
[string[]] $storageAccountRoles = @(
'Storage Blob Data Contributor',
'Contributor'
);
[Void](Set-AzContext -Subscription $Subscription);
$servicePrincipal = Get-AzADServicePrincipal -ApplicationId $ClientId;
$storageAccount = Get-AzStorageAccount -ResourceGroupName $ResourceGroupName -Name $StorageAccountName -ErrorAction SilentlyContinue;
if ((Get-AzResourceGroup -Name $ResourceGroupName -ErrorAction SilentlyContinue) -eq $null) {
Write-Host "Creating resource group";
[Void](New-AzResourceGroup -Name $ResourceGroupName -Location $Location);
}
if ($storageAccount -eq $null) {
Write-Host "Creating storage account";
$storageAccount = New-AzStorageAccount -ResourceGroupName $ResourceGroupName -Name $StorageAccountName -Location $Location -SkuName 'Standard_LRS';
}
foreach ($role in $storageAccountRoles) {
$roleAssignment = Get-AzRoleAssignment -ObjectId $servicePrincipal.Id -RoleDefinitionName $role -Scope $storageAccount.Id -ErrorAction SilentlyContinue;
if ($roleAssignment -eq $null) {
Write-Host "Assigning '$role' role to Service Principal $($servicePrincipal.DisplayName) over '$($storageAccount.Name)' storage account";
[Void](New-AzRoleAssignment -ObjectId $servicePrincipal.Id -RoleDefinitionName $role -Scope $storageAccount.Id);
}
}
$result = [PSCustomObject]@{
StorageAccount = $storageAccount.StorageAccountName;
StorageAccountResourceGroupName = $storageAccount.ResourceGroupName;
}
Write-Host "";
Write-Host "Please share below JSON:";
Write-Host ($result | ConvertTo-Json) -ForegroundColor Green;
4 - Granting access to cost data via PowerShell
Use the script below to create required Azure resources (e.g., storage account) and grant access to cost data via cost management exports. The script assigns billing roles (e.g., 'Enrollment Reader' or 'Billing account reader') to grant access to price sheet data, enabling use of customer-specific pricing over the default retail prices.
The script will create resources from the Parameters section that don't exist and write info to the console.
Copy the script into a text editor, change the initial values if needed, and run the edited script in an authenticated PowerShell terminal.
Ensure you're authenticated with sufficient privileges (e.g., Owner or Contributor) in Azure.
#region Parameters
[string] $ExportName = 'BKBScheduledExport'
[string] $StorageAccountName = 'saxxxxxxx'
[string] $ContainerName = 'bkb-cost-exports'
[string] $FolderName = 'bkb-amortized-cost-export'
[string] $SubscriptionId = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
[string] $ResourceGroupName = 'rg-bkb-cloud'
[string] $Location = 'xxxxx' # eastus, westeurope, ...
[string] $ClientId = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' # App registration (service principal) Application ID
[string] $BillingAccountDisplayName = 'XXX XXX'
#endregion
$ErrorActionPreference = 'Stop';
[Void](Set-AzContext -Subscription $SubscriptionId);
$billingAccount = Get-AzBillingAccount | where { $_.DisplayName -eq $BillingAccountDisplayName };
$servicePrincipal = Get-AzADServicePrincipal -ApplicationId $ClientId;
[bool] $IsEnterpriseLike = ($billingAccount.AgreementType -in @('EnterpriseAgreement', 'MicrosoftOnlineServicesProgram'));
$storageAccount = Get-AzStorageAccount -ResourceGroupName $ResourceGroupName -Name $StorageAccountName -ErrorAction SilentlyContinue;
$currentTime = Get-Date -AsUTC;
[string] $resourceUrl = 'https://management.azure.com';
if ((Get-AzResourceGroup -Name $ResourceGroupName -ErrorAction SilentlyContinue) -eq $null) {
Write-Host "Creating resource group";
[Void](New-AzResourceGroup -Name $ResourceGroupName -Location $Location);
}
if ($storageAccount -eq $null) {
Write-Host "Creating storage account";
$storageAccount = New-AzStorageAccount -ResourceGroupName $ResourceGroupName -Name $StorageAccountName -Location $Location -SkuName 'Standard_LRS';
}
$token = (ConvertFrom-SecureString (Get-AzAccessToken -ResourceUrl $resourceUrl -AsSecureString).Token -AsPlainText);
$exportUri = "$resourceUrl/$($billingAccount.Id)/providers/Microsoft.CostManagement/exports/$($ExportName)?api-version=2025-03-01";
$restHeaders = @{ Authorization = "Bearer $($token)"; 'Content-Type' = 'application/json'; };
try { $export = Invoke-RestMethod -Uri $exportUri -Method Get -Headers $restHeaders; } catch {}
if ($export -eq $null) {
$providerName = 'Microsoft.CostManagementExports';
$provider = Get-AzResourceProvider -ProviderNamespace $providerName;
if ($provider.RegistrationState -ne 'Registered') {
Write-Host "Registering $providerName resource provider on $SubscriptionId subscription";
[Void](Register-AzResourceProvider -ProviderNamespace $providerName);
while ((Get-AzResourceProvider -ProviderNamespace $providerName).RegistrationState -ne 'Registered') {
Write-Host "Waiting for $providerName resource provider registration...";
Start-Sleep -Seconds 5;
}
}
}
Write-Host "Creating / Updating Export";
$payload = @{
eTag = if ($export) { $export.eTag } else { $null };
identity = @{
type = 'SystemAssigned';
};
location = $Location;
properties = @{
schedule = @{
status = 'Active';
recurrence = 'Daily';
recurrencePeriod = @{
from = $currentTime.ToString('yyyy-MM-ddTHH:mm:ssZ');
to = $currentTime.AddYears(10).ToString('yyyy-MM-ddTHH:mm:ssZ');
};
};
format = 'Csv';
deliveryInfo = @{
destination = @{
type = 'AzureBlob';
resourceId = $storageAccount.Id;
container = $ContainerName;
rootFolderPath = $FolderName;
};
};
definition = @{
type = 'AmortizedCost';
timeframe = 'MonthToDate';
dataSet = @{
granularity = 'Daily';
};
};
partitionData = $true;
dataOverwriteBehavior = 'OverwritePreviousReport';
compressionMode = 'gzip';
};
};
$export = Invoke-RestMethod -Uri $exportUri -Method Put -Headers $restHeaders -Body ($payload | ConvertTo-Json -Depth 99);
# Backfill for previous two months and current
$fromCurrent = (Get-Date -Date $currentTime -Day 1 -Hour 0 -Minute 0 -Second 0 -Millisecond 0);
$periods = @();
for ($i = 2; $i -ge 0; $i--) {
$from = $fromCurrent.AddMonths(-$i);
$to = $from.AddMonths(1).AddTicks(-1);
$periods += @{from=$from; to=$to};
}
foreach ($period in $periods) {
$export = Invoke-RestMethod -Uri $exportUri -Method Get -Headers $restHeaders;
$payload = @{
eTag = $export.eTag;
identity = @{ type = 'SystemAssigned' };
location = $Location;
properties = @{
schedule = $export.properties.schedule;
format = 'Csv';
deliveryInfo = $export.properties.deliveryInfo;
definition = @{
type = 'AmortizedCost';
timeframe = 'Custom';
timePeriod = @{
from = $period.from.ToString('yyyy-MM-ddTHH:mm:ssZ');
to = $period.to.ToString('yyyy-MM-ddTHH:mm:ssZ');
};
dataSet = @{ granularity = 'Daily' };
};
partitionData = $true;
dataOverwriteBehavior = 'OverwritePreviousReport';
compressionMode = 'gzip';
};
};
[Void](Invoke-RestMethod -Uri $exportUri -Method Put -Headers $restHeaders -Body ($payload | ConvertTo-Json -Depth 99));
$executeUri = $exportUri -replace '\?api-version.*', '/execute?api-version=2025-03-01';
$attempt = 0;
$maxAttempts = 10;
do {
try {
[Void](Invoke-RestMethod -Uri $executeUri -Method Post -Headers $restHeaders);
Write-Host "Triggered export for $($period.from.ToString('yyyy-MM-dd')) to $($period.to.ToString('yyyy-MM-dd'))";
break;
} catch {
$attempt++;
if ($attempt -ge $maxAttempts) { throw $_; }
Write-Host "Error triggering for $($period.from.ToString('yyyy-MM-dd')), retry $attempt";
Start-Sleep -Seconds 10;
}
} while ($true);
}
# Set back to MonthToDate
$export = Invoke-RestMethod -Uri $exportUri -Method Get -Headers $restHeaders;
$payload = @{
eTag = $export.eTag;
identity = @{ type = 'SystemAssigned' };
location = $Location;
properties = @{
schedule = @{
status = 'Active';
recurrence = 'Daily';
recurrencePeriod = @{
from = $currentTime.ToString('yyyy-MM-ddTHH:mm:ssZ');
to = $currentTime.AddYears(10).ToString('yyyy-MM-ddTHH:mm:ssZ');
};
};
format = 'Csv';
deliveryInfo = @{
destination = @{
type = 'AzureBlob';
resourceId = $storageAccount.Id;
container = $ContainerName;
rootFolderPath = $FolderName;
};
};
definition = @{
type = 'AmortizedCost';
timeframe = 'MonthToDate';
dataSet = @{
granularity = 'Daily';
};
};
partitionData = $true;
dataOverwriteBehavior = 'OverwritePreviousReport';
compressionMode = 'gzip';
};
};
[Void](Invoke-RestMethod -Uri $exportUri -Method Put -Headers $restHeaders -Body ($payload | ConvertTo-Json -Depth 99));
$permissions = @(
[PSCustomObject]@{
RoleName = 'Reservations Reader';
Scope = '/providers/Microsoft.Capacity';
}, [PSCustomObject]@{
RoleName = 'Savings plan Reader';
Scope = '/providers/Microsoft.BillingBenefits';
}, [PSCustomObject]@{
RoleName = 'Storage Blob Data Contributor';
Scope = $storageAccount.Id;
}, [PSCustomObject]@{
RoleName = 'Contributor';
Scope = $storageAccount.Id;
});
foreach ($permission in $permissions) {
$roleAssignment = Get-AzRoleAssignment -ObjectId $servicePrincipal.Id -RoleDefinitionName $permission.RoleName -Scope $permission.Scope -ErrorAction SilentlyContinue;
if ($roleAssignment -eq $null) {
Write-Host "Assigning '$($permission.RoleName)' role to Service Principal $($servicePrincipal.DisplayName) over '$($permission.Scope)' scope";
[Void](New-AzRoleAssignment -ObjectId $servicePrincipal.Id -RoleDefinitionName $permission.RoleName -Scope $permission.Scope);
}
}
$apiVersion = '?api-version=2020-05-01';
$billingRoleDefinitions = Invoke-RestMethod -Uri "$($resourceUrl)$($billingAccount.Id)/billingRoleDefinitions$($apiVersion)" `
-Method Get -Headers $restHeaders;
$existingAssignments = (Invoke-RestMethod -Uri "$($resourceUrl)$($billingAccount.Id)/billingRoleAssignments$($apiVersion)" `
-Method Get -Headers $restHeaders).value;
if ($IsEnterpriseLike) {
$readerRole = $billingRoleDefinitions.value | where {$_.properties.roleName -eq 'Enrollment Reader'};
$existingAssignments = ($existingAssignments | where { $_.properties.principalId -eq $servicePrincipal.Id -and $_.properties.roleDefinitionId -eq $readerRole.id });
if ($existingAssignments -eq $null) {
Write-Host "Assigning 'Enrollment Reader' role to Service Principal $($servicePrincipal.DisplayName) on $($billingAccount.Name) billing account";
$payload = @{
properties = @{
principalId = $servicePrincipal.Id;
principalTenantId = (Get-AzContext).Tenant.Id;
roleDefinitionId = $readerRole.Id;
}
};
$result = Invoke-RestMethod -Uri "$($resourceUrl)$($billingAccount.Id)/billingRoleAssignments/$((New-Guid).Guid)?api-version=2019-10-01-preview" `
-Method Put -Headers $restHeaders -Body ($payload | ConvertTo-Json -Depth 99);
}
} else {
$readerRole = $billingRoleDefinitions.value | Where-Object { $_.properties.roleName -eq 'Billing account reader' };
$existingAssignments = ($existingAssignments | where { $_.properties.principalId -eq $servicePrincipal.Id -and $_.properties.roleDefinitionId -eq $readerRole.id });
if ($existingAssignments -eq $null) {
Write-Host "Assigning 'Billing account reader' role to Service Principal $($servicePrincipal.DisplayName) on $($billingAccount.Name) billing account";
$payload = @{
roleDefinitionId = $readerRole.id;
principalId = $servicePrincipal.Id;
scope = $billingAccount.Id;
principalTenantId = (Get-AzContext).Tenant.Id;
};
$result = Invoke-RestMethod -Uri "$($resourceUrl)$($billingAccount.Id)/createBillingRoleAssignment$($apiVersion)" `
-Method Post -Headers $restHeaders -Body ($payload | ConvertTo-Json -Depth 99);
}
}
$result = [PSCustomObject]@{
BillingAccount = $billingAccount.Name;
StorageAccount = $storageAccount.StorageAccountName;
Container = $ContainerName;
Folder = $FolderName;
ExportName = $export.Name;
ExportId = $export.Id;
StorageAccountResourceGroupName = $storageAccount.ResourceGroupName;
}
Write-Host "";
Write-Host "Please share below JSON:";
Write-Host ($result | ConvertTo-Json) -ForegroundColor Green;