How to Change the Storage Account or Export Configuration for Azure Billing Data
Azure Cost Management exports deliver billing data to Azure Storage on a scheduled basis. As your organization's needs evolve, you may need to change the destination storage account, modify export settings, or reconfigure data retention policies. This guide explains how to safely update or recreate cost export configurations without losing historical data or disrupting analytics pipelines.
Common scenarios include migrating to a new storage account for better organization, changing export frequency, adding or removing cost columns, or consolidating exports from multiple subscriptions into a centralized FinOps storage account.
Prerequisites
Before you begin, ensure you have:
- Cost Management Contributor or Billing Account Contributor role
- Access to the Azure portal (portal.azure.com)
- Storage Blob Data Contributor role on both the current and new storage accounts
- Knowledge of existing export configurations
- PowerShell 7.0+ or Azure CLI installed (for automation)
- Backup plan for existing export data
Understanding Export Configuration Options
Azure Cost Management exports support various configuration options:
Export Scope
- Subscription: Single subscription costs
- Resource Group: Specific resource group costs
- Billing Account: All subscriptions under a billing account
- Management Group: Aggregated costs across multiple subscriptions
Export Types
- Actual Cost: Pay-as-you-go pricing
- Amortized Cost: Reservation and savings plan costs spread over time
- Usage: Detailed usage metrics without pricing
Export Frequency
- Daily: New file created each day with previous day's costs
- Monthly: Single file generated monthly
- One-time: Ad-hoc export for specific date range
Step-by-Step Guide
Method 1: Modify Existing Export via Azure Portal
Step 1: Navigate to Current Export
- Sign in to the Azure portal
- Navigate to Cost Management + Billing
- Select your billing scope (subscription, billing account, etc.)
- Click Exports in the left menu under Cost Management
- Locate the export you want to modify
Step 2: Review Current Configuration
- Click on the export name to view details
- Note the current settings:
- Storage account name and container
- Export frequency and schedule
- Data type (Actual, Amortized, Usage)
- Date range
- File format
Step 3: Modify Export Settings
Unfortunately, Azure doesn't allow direct modification of storage account destination. You must delete and recreate the export. However, you can modify:
- Schedule: Click Edit next to Schedule
- Date range: Modify the time period
- Status: Enable or disable the export
For storage account changes, proceed to Method 2.
Method 2: Recreate Export with New Storage Account (Portal)
Step 1: Prepare New Storage Account
First, ensure the new storage account exists and is properly configured:
# Create new storage account if needed
$resourceGroupName = "finops-rg"
$storageAccountName = "newcostmgmtstorage"
$location = "eastus"
New-AzStorageAccount `
-ResourceGroupName $resourceGroupName `
-Name $storageAccountName `
-Location $location `
-SkuName Standard_LRS `
-Kind StorageV2 `
-AccessTier Hot `
-AllowBlobPublicAccess $false
# Create container for exports
$ctx = (Get-AzStorageAccount -ResourceGroupName $resourceGroupName -Name $storageAccountName).Context
New-AzStorageContainer -Name "cost-exports" -Context $ctx -Permission Off
Step 2: Document Current Export Configuration
Before deleting the old export, save its configuration:
# Export current configuration to JSON
$scope = "/subscriptions/12345678-1234-1234-1234-123456789012"
$exportName = "daily-cost-export"
$currentExport = Get-AzCostManagementExport -Scope $scope -Name $exportName
$currentExport | ConvertTo-Json -Depth 10 | Out-File "export-backup-$(Get-Date -Format 'yyyyMMdd').json"
Step 3: Copy Historical Data to New Storage Account
Before deleting the old export, copy existing data:
# Use AzCopy to transfer data
azcopy login
# Copy all existing export data
azcopy copy \
"https://oldstorageaccount.blob.core.windows.net/cost-exports/*" \
"https://newcostmgmtstorage.blob.core.windows.net/cost-exports/" \
--recursive=true
Or using PowerShell:
# Get source and destination contexts
$sourceAccount = "oldstorageaccount"
$destAccount = "newcostmgmtstorage"
$containerName = "cost-exports"
$sourceCtx = New-AzStorageContext -StorageAccountName $sourceAccount -UseConnectedAccount
$destCtx = New-AzStorageContext -StorageAccountName $destAccount -UseConnectedAccount
# Copy all blobs
$blobs = Get-AzStorageBlob -Container $containerName -Context $sourceCtx
foreach ($blob in $blobs) {
Copy-AzStorageBlob `
-SrcContainer $containerName `
-SrcBlob $blob.Name `
-DestContainer $containerName `
-DestBlob $blob.Name `
-SrcContext $sourceCtx `
-DestContext $destCtx `
-Force
Write-Host "Copied: $($blob.Name)"
}
Step 4: Create New Export with Updated Configuration
In the Azure Portal:
- Navigate to Cost Management + Billing > Exports
- Click + Add or + Create
- Configure the new export:
- Name: Use the same name or a new descriptive name
- Export type: Select Actual Cost, Amortized Cost, or Usage
- Frequency: Daily, Monthly, or One-time
- Time period: Month-to-date, Last month, or Custom
- Under Storage account details:
- Subscription: Select the subscription containing the new storage account
- Storage account: Select the new storage account
- Container: Select the container for exports
- Directory: (Optional) Specify a folder path
- Click Create
Step 5: Verify New Export
- Wait for the first export to complete (up to 24 hours for daily exports)
- Navigate to the new storage account
- Browse to the container and verify files are being created
- Compare file structure with backed-up configuration
Step 6: Delete Old Export (Optional)
Once the new export is working correctly:
- Navigate to the old export
- Click Delete
- Confirm deletion
- Keep the old storage account until you've verified all data integrity
Method 3: Change Export Configuration Using PowerShell
# Connect to Azure
Connect-AzAccount
# Define scope and export details
$scope = "/subscriptions/12345678-1234-1234-1234-123456789012"
$oldExportName = "daily-cost-export"
$newExportName = "daily-cost-export-v2"
# Get current export configuration
$currentExport = Get-AzCostManagementExport -Scope $scope -Name $oldExportName
# Define new storage account
$newStorageAccountId = "/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/finops-rg/providers/Microsoft.Storage/storageAccounts/newcostmgmtstorage"
$containerName = "cost-exports"
$directory = "subscription-exports"
# Create new export with updated configuration
$newExportParams = @{
Scope = $scope
Name = $newExportName
ScheduleStatus = "Active"
ScheduleRecurrence = "Daily"
RecurrencePeriodFrom = (Get-Date).ToString("yyyy-MM-ddT00:00:00Z")
RecurrencePeriodTo = (Get-Date).AddYears(5).ToString("yyyy-MM-ddT00:00:00Z")
Format = "Csv"
DefinitionType = "ActualCost"
DefinitionTimeframe = "MonthToDate"
DestinationResourceId = $newStorageAccountId
DestinationContainer = $containerName
DestinationRootFolderPath = $directory
}
# Create the new export
New-AzCostManagementExport @newExportParams
Write-Host "New export '$newExportName' created successfully"
# Optionally delete the old export
# Remove-AzCostManagementExport -Scope $scope -Name $oldExportName
Method 4: Update Multiple Exports Using Azure CLI Script
For organizations with many exports, automate the migration:
#!/bin/bash
# Configuration
OLD_STORAGE_ACCOUNT="oldstorageaccount"
NEW_STORAGE_ACCOUNT="newcostmgmtstorage"
RESOURCE_GROUP="finops-rg"
SUBSCRIPTION_ID="12345678-1234-1234-1234-123456789012"
# Get new storage account resource ID
NEW_STORAGE_ID="/subscriptions/${SUBSCRIPTION_ID}/resourceGroups/${RESOURCE_GROUP}/providers/Microsoft.Storage/storageAccounts/${NEW_STORAGE_ACCOUNT}"
# List all exports for the subscription
SCOPE="/subscriptions/${SUBSCRIPTION_ID}"
echo "Listing all exports for scope: ${SCOPE}"
EXPORTS=$(az costmanagement export list --scope "${SCOPE}" --query "[].name" -o tsv)
for EXPORT_NAME in $EXPORTS; do
echo "Processing export: ${EXPORT_NAME}"
# Get export details
EXPORT_JSON=$(az costmanagement export show --name "${EXPORT_NAME}" --scope "${SCOPE}")
# Extract configuration
EXPORT_TYPE=$(echo $EXPORT_JSON | jq -r '.definition.type')
RECURRENCE=$(echo $EXPORT_JSON | jq -r '.schedule.recurrence')
CONTAINER=$(echo $EXPORT_JSON | jq -r '.deliveryInfo.destination.container')
DIRECTORY=$(echo $EXPORT_JSON | jq -r '.deliveryInfo.destination.rootFolderPath')
# Create new export name
NEW_EXPORT_NAME="${EXPORT_NAME}-migrated"
echo "Creating new export: ${NEW_EXPORT_NAME}"
# Create new export
az costmanagement export create \
--name "${NEW_EXPORT_NAME}" \
--scope "${SCOPE}" \
--storage-account-id "${NEW_STORAGE_ID}" \
--storage-container "${CONTAINER}" \
--storage-directory "${DIRECTORY}" \
--timeframe "MonthToDate" \
--type "${EXPORT_TYPE}" \
--recurrence "${RECURRENCE}"
echo "Completed: ${NEW_EXPORT_NAME}"
done
echo "Migration complete!"
Method 5: Change Export Column Configuration
To add or remove columns from exports, recreate the export with specific column selections:
# Define specific columns to include
$columns = @(
"Date",
"ResourceId",
"ResourceLocation",
"ResourceGroupName",
"ResourceType",
"MeterCategory",
"MeterSubcategory",
"Meter",
"Cost",
"UnitPrice",
"Quantity",
"Tags"
)
# Create export with custom columns
$exportParams = @{
Scope = $scope
Name = "custom-column-export"
ScheduleStatus = "Active"
ScheduleRecurrence = "Daily"
RecurrencePeriodFrom = (Get-Date).ToString("yyyy-MM-ddT00:00:00Z")
RecurrencePeriodTo = (Get-Date).AddYears(1).ToString("yyyy-MM-ddT00:00:00Z")
Format = "Csv"
DefinitionType = "Usage"
DefinitionTimeframe = "MonthToDate"
DatasetColumn = $columns
DestinationResourceId = $storageAccountId
DestinationContainer = "cost-exports"
DestinationRootFolderPath = "custom-exports"
}
New-AzCostManagementExport @exportParams
Best Practices
1. Test Before Migrating Production Exports
Create a test export to the new storage account first:
# Create test export
$testExportParams = @{
Scope = $scope
Name = "test-export-migration"
ScheduleStatus = "Active"
ScheduleRecurrence = "Daily"
RecurrencePeriodFrom = (Get-Date).ToString("yyyy-MM-ddT00:00:00Z")
RecurrencePeriodTo = (Get-Date).AddDays(7).ToString("yyyy-MM-ddT00:00:00Z")
Format = "Csv"
DefinitionType = "ActualCost"
DefinitionTimeframe = "MonthToDate"
DestinationResourceId = $newStorageAccountId
DestinationContainer = "cost-exports-test"
DestinationRootFolderPath = "test"
}
New-AzCostManagementExport @testExportParams
2. Implement Export Versioning
Use directory paths to track export configuration changes:
cost-exports/
├── v1/
│ └── 2024-01/
│ └── export-data.csv
├── v2/
│ └── 2024-02/
│ └── export-data.csv
└── current/ (symlink or latest version)
3. Maintain Change Log
Document all export configuration changes:
# Create change log
$changeLog = @{
Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
ExportName = $exportName
ChangeType = "Storage Account Migration"
OldStorageAccount = $oldStorageAccount
NewStorageAccount = $newStorageAccount
ModifiedBy = $env:USERNAME
Reason = "Consolidating to centralized FinOps storage"
}
$changeLog | ConvertTo-Json | Add-Content "export-change-log.json"
4. Configure Lifecycle Management on New Storage Account
Set up automatic archival for old exports:
# Create lifecycle management rule
$rule = New-AzStorageAccountManagementPolicyRule `
-Name "archive-old-exports" `
-Blob `
-TierToCool -DaysAfterModificationGreaterThan 90 `
-TierToArchive -DaysAfterModificationGreaterThan 180 `
-Delete -DaysAfterModificationGreaterThan 365
$policy = Set-AzStorageAccountManagementPolicy `
-ResourceGroupName $resourceGroupName `
-StorageAccountName $newStorageAccount `
-Rule $rule
5. Set Up Alerts for Export Failures
Create alerts to monitor export health:
# Create action group for notifications
az monitor action-group create \
--name "cost-export-alerts" \
--resource-group $RESOURCE_GROUP \
--short-name "CostAlert" \
--email "[email protected]"
# Create alert rule (requires Log Analytics workspace)
az monitor metrics alert create \
--name "cost-export-failure-alert" \
--resource-group $RESOURCE_GROUP \
--scopes $NEW_STORAGE_ID \
--condition "total BlobCount < 1" \
--window-size 24h \
--evaluation-frequency 1h \
--action cost-export-alerts
6. Validate Data Integrity After Migration
Compare row counts and cost totals:
# Compare old vs new export data
$oldData = Import-Csv "old-export.csv"
$newData = Import-Csv "new-export.csv"
$comparison = [PSCustomObject]@{
OldRowCount = $oldData.Count
NewRowCount = $newData.Count
OldTotalCost = ($oldData | Measure-Object -Property Cost -Sum).Sum
NewTotalCost = ($newData | Measure-Object -Property Cost -Sum).Sum
RowCountDiff = $newData.Count - $oldData.Count
CostDiff = ($newData | Measure-Object -Property Cost -Sum).Sum - ($oldData | Measure-Object -Property Cost -Sum).Sum
}
$comparison | Format-Table
Troubleshooting
Issue: "Permission denied" when creating export to new storage account
Cause: Missing IAM permissions on the new storage account.
Solution:
# Grant Cost Management service principal access to storage account
$storageAccountId = "/subscriptions/$subscriptionId/resourceGroups/$resourceGroupName/providers/Microsoft.Storage/storageAccounts/$storageAccountName"
# Get Cost Management service principal (varies by region)
# For global: f7f7e7e7-e7e7-e7e7-e7e7-e7e7e7e7e7e7
New-AzRoleAssignment `
-ObjectId "f7f7e7e7-e7e7-e7e7-e7e7-e7e7e7e7e7e7" `
-RoleDefinitionName "Storage Blob Data Contributor" `
-Scope $storageAccountId
Issue: Historical data missing after migration
Cause: Export data wasn't copied before deleting old export.
Solution:
- If old storage account still exists, copy data immediately
- If deleted, check for backup or soft-delete:
# Check for soft-deleted storage accounts Get-AzResource -ResourceType "Microsoft.Storage/deletedAccounts" | Where-Object { $_.Name -eq $oldStorageAccount } # Restore if found (within 90-day retention) Restore-AzStorageAccount -Name $oldStorageAccount -ResourceGroupName $resourceGroupName
Issue: New export has different file structure
Cause: Export type or column configuration changed.
Solution:
-
Verify export configuration matches original:
Compare-Object ` (Get-Content "export-backup.json" | ConvertFrom-Json | Select-Object -ExpandProperty Definition) ` (Get-AzCostManagementExport -Scope $scope -Name $newExportName | Select-Object -ExpandProperty Definition)
-
Recreate export with exact configuration from backup
Issue: Downstream tools fail after storage account change
Cause: Hard-coded storage account references in pipelines.
Solution:
-
Update connection strings in:
- Azure Synapse linked services
- Power BI data sources
- Azure Data Factory pipelines
- Custom scripts and automation
-
Use environment variables or Azure Key Vault for storage account references:
# Store connection string in Key Vault $connectionString = (Get-AzStorageAccount -ResourceGroupName $resourceGroupName -Name $newStorageAccount | Get-AzStorageAccountKey | Select-Object -First 1).Value Set-AzKeyVaultSecret ` -VaultName "finops-keyvault" ` -Name "CostExportStorageConnectionString" ` -SecretValue (ConvertTo-SecureString $connectionString -AsPlainText -Force)
Issue: Export running but no data appearing in new storage account
Cause: Export schedule hasn't triggered yet or permissions issue.
Solution:
-
Manually trigger the export:
az costmanagement export run --name $EXPORT_NAME --scope $SCOPE
-
Check export run history:
Get-AzCostManagementExport -Scope $scope -Name $exportName | Select-Object -ExpandProperty RunHistory
-
Verify storage account firewall settings allow Cost Management service
Next Steps
After successfully changing your export configuration, consider these related tasks:
- Secure the new storage account: How to Secure Cost Management Data in Azure Storage and Synapse
- Monitor export status: How to Monitor Cost Export Status and Data Freshness in Azure
- Configure IAM roles: How to Grant IAM Roles for Azure Cost Management Data Export
- Update analytics pipelines: How to View Azure Billing Data in Azure Synapse or Power BI
Related Resources
Frequently Asked Questions
Find answers to common questions
Need Professional Help?
Our team of experts can help you implement and configure these solutions for your organization.