Skip to content

Commit

Permalink
FIx the New-FinOpsCostExport (#1259)
Browse files Browse the repository at this point in the history
Co-authored-by: Michael Flanakin <[email protected]>
Co-authored-by: Michael Flanakin <[email protected]>
  • Loading branch information
3 people authored Jan 31, 2025
1 parent 89bc390 commit 4c9b65c
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 36 deletions.
38 changes: 37 additions & 1 deletion docs/_resources/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ Legend:
> 1. Enabled "Export to CSV" option on the **Idle backups** query.
> 1. Corrected VM processor details on the **Compute** tab query.

<br><a name="latest"></a>

## 🚚 v0.8
Expand Down Expand Up @@ -175,6 +174,43 @@ Legend:
>
> 1. Improved multi-tenancy support with Azure Lighthouse guidance ([#1036](https://github.com/microsoft/finops-toolkit/issues/1036))
🖥️ PowerShell
{: .fs-5 .fw-500 .mt-4 mb-0 }

> ➕ Added:
>
> 1. Added explicit `-CommitmentDiscountScope`, `-CommitmentDiscountResourceType`, and `-CommitmentDiscountLookback` parameters to the [New-FinOpsCostExport command](../_automation/powershell/cost/New-FinOpsCostExport.md) for reservation recommendations.
> 1. Added explicit `-SystemAssignedIdentity` switch parameter to the [New-FinOpsCostExport command](../_automation/powershell/cost/New-FinOpsCostExport.md) to enable system-assigned identity.
>
> ✏️ Changed:
>
> 1. Updated the following `RunHistory` array item properties in [Get-FinOpsCostExport command](../_automation/powershell/cost/Get-FinOpsCostExport.md) outputs:
> - Renamed `Id` to `ResourceId`
> - Renamed `StartTime` to `RunStartTime`
> - Renamed `EndTime` to `RunEndTime`
> - Added `RunId` with the GUID export run ID
> - Added `QueryStartDate` with the first day of the exported data
> - Added `QueryEndDate` with the last day of the exported data
> - Added `ErrorCode` with the error code of the run, if applicable
> - Added `ErrorMessage` with the error message of the run, if applicable
> 1. Fixed the following [Get-FinOpsCostExport command](../_automation/powershell/cost/Get-FinOpsCostExport.md) outputs:
> - `DatasetVersion` string
> - `DatasetFilters` object
> - `OverwriteData` flag
> - `PartitionData` flag
> - `CompressionMode` flag
> - `RunHistory` array item properties:
> - `FileName` string
> - `SubmittedBy` string
> - `SubmittedTime` date/time
> - `Status` string
> - `StartTime` date/time (renamed to `RunStartTime`)
> - `EndTime` date/time (renamed to `RunEndTime`)
>
> 🛠️ Fixed:
>
> 1. Fixed the [New-FinOpsCostExport command](../_automation/powershell/cost/New-FinOpsCostExport.md) to work for prices, reservation recommendations, and reservation transactions. ([#1193](https://github.com/microsoft/finops-toolkit/issues/1193)).
🌐 Open data
{: .fs-5 .fw-500 .mt-4 mb-0 }

Expand Down
37 changes: 21 additions & 16 deletions src/powershell/Public/Get-FinOpsCostExport.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ function Get-FinOpsCostExport
$Scope,

[Parameter()]
[ValidateSet("ActualCost", "AmortizedCost", "FocusCost", "PriceSheet", "ReservationDetails", "ReservationTransactions", "ReservationRecommendations")]
[ValidateSet("ActualCost", "AmortizedCost", "FocusCost", "PriceSheet", "ReservationDetails", "ReservationRecommendations", "ReservationTransactions")]
[string]
$Dataset,

Expand Down Expand Up @@ -174,12 +174,12 @@ function Get-FinOpsCostExport
eTag = $_.eTag
Description = $_.properties.exportDescription
Dataset = $_.properties.definition.type
DatasetVersion = $_.properties.definition.configuration.dataVersion
DatasetFilters = $_.properties.definition.configuration.filter
DatasetVersion = $_.properties.definition.dataSet.configuration.dataVersion
DatasetFilters = $_.properties.definition.dataSet.configuration.filter
DatasetTimeFrame = $_.properties.definition.timeframe
DatasetStartDate = $_.properties.definition.timePeriod.from
DatasetEndDate = $_.properties.definition.timePeriod.to
DatasetGranularity = $_.properties.definition.dataset.granularity
DatasetGranularity = $_.properties.definition.dataSet.granularity
ScheduleStatus = $_.properties.schedule.status
ScheduleRecurrence = $_.properties.schedule.recurrence
ScheduleStartDate = $_.properties.schedule.recurrencePeriod.from
Expand All @@ -188,20 +188,25 @@ function Get-FinOpsCostExport
Format = $_.properties.format
StorageAccountId = $_.properties.deliveryInfo.destination.resourceId
StorageContainer = $_.properties.deliveryInfo.destination.container
StoragePath = $_.properties.deliveryInfo.destination.rootfolderpath
OverwriteData = $_.properties.deliveryInfo.dataOverwriteBehavior -eq "OverwritePreviousReport"
PartitionData = $_.properties.deliveryInfo.partitionData
CompressionMode = $_.properties.deliveryInfo.compressionMode
StoragePath = $_.properties.deliveryInfo.destination.rootFolderPath
OverwriteData = $_.properties.dataOverwriteBehavior -eq "OverwritePreviousReport"
PartitionData = $_.properties.partitionData
CompressionMode = $_.properties.compressionMode
RunHistory = $_.properties.runHistory.value | Where-Object { $_ -ne $null } | ForEach-Object {
[PSCustomObject]@{
Id = $_.id
ExecutionType = $_.properties.executionType
FileName = $_.fileName
StartTime = $_.processingStartTime
EndTime = $_.processingEndTime
Status = $_.status
SubmittedBy = $_.submittedBy
SubmittedTime = $_.submittedTime
ResourceId = $_.id
RunId = $_.name
ExecutionType = $_.properties.executionType
Status = $_.properties.status
SubmittedBy = $_.properties.submittedBy
SubmittedTime = $_.properties.submittedTime
RunStartTime = $_.properties.processingStartTime
RunEndTime = $_.properties.processingEndTime
FileName = $_.properties.fileName
QueryStartDate = $_.properties.startDate
QueryEndDate = $_.properties.endDate
ErrorCode = $_.properties.error.code
ErrorMessage = $_.properties.error.message
}
}
}
Expand Down
59 changes: 54 additions & 5 deletions src/powershell/Public/New-FinOpsCostExport.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,25 @@
Required. Resource ID of the scope to export data for.
.PARAMETER Dataset
Optional. Dataset to export. Allowed values = "ActualCost", "AmortizedCost", "FocusCost", "PriceSheet", "ReservationDetails", "ReservationTransactions", "ReservationRecommendations". Default = "FocusCost".
Optional. Dataset to export. Allowed values = "ActualCost", "AmortizedCost", "FocusCost", "PriceSheet", "ReservationDetails", "ReservationRecommendations", "ReservationTransactions". Default = "FocusCost".
.PARAMETER DatasetVersion
Optional. Schema version of the dataset to export. Default = "1.0" (applies to FocusCost only).
.PARAMETER DatasetFilters
Optional. Dictionary of key/value pairs to filter the dataset with. Only applies to ReservationRecommendations dataset in 2023-07-01-preview. Valid filters are reservationScope (Shared or Single), resourceType (e.g., VirtualMachines), lookBackPeriod (Last7Days, Last30Days, Last60Days).
.PARAMETER CommitmentDiscountScope
Optional. Reservation scope filter to use when exporting reservation recommendations. Ignored for other export types. Allowed values: Shared, Single. Default: Shared.
.PARAMETER CommitmentDiscountResourceType
Optional. Reservation resource type filter to use when exporting reservation recommendations. Ignored for other export types. Default: VirtualMachines.
.PARAMETER CommitmentDiscountLookback
Optional. Reservation resource type filter to use when exporting reservation recommendations. Ignored for other export types. Allowed values: 7, 30, 60. Default: 30.
.PARAMETER Monthly
Optional. Indicates that the export should be executed monthly (instead of daily). Default = false.
Optional. Indicates that the export should be executed monthly (instead of daily). Ignored for prices, reservation recommendations, and reservation transactions. Default = false.
.PARAMETER OneTime
Optional. Indicates that the export should only be executed once. When set, the start/end dates are the dates to query data for. Cannot be used in conjunction with the -Monthly option.
Expand All @@ -54,6 +63,9 @@
.PARAMETER DoNotOverwrite
Optional. Indicates whether to overwrite previously exported data for the current month. Overwriting is recommended to keep storage size and costs down so this option is to disable overwriting. If creating an export for FinOps hubs, we recommend you specify the -DoNotOverwrite option to improve troubleshooting. Default = false.
.PARAMETER SystemAssignedIdentity
Optional. Indicates that managed identity should be used to push data to the storage account. Managed identity is required in order to work with storage accounts behind a firewall but require access to grant permissions (e.g., Owner). If specified, managed identity will be used; otherwise, managed identity will not be used and your export will not be able to push data to a storage account behind a firewall. Default = (empty).
.PARAMETER Location
Optional. Indicates the Azure location to use for the managed identity used to push data to the storage account. Managed identity is required in order to work with storage accounts behind a firewall but require access to grant permissions (e.g., Owner). If specified, managed identity will be used; otherwise, managed identity will not be used and your export will not be able to push data to a storage account behind a firewall. Default = (empty).
Expand Down Expand Up @@ -128,7 +140,7 @@ function New-FinOpsCostExport
$Scope,

[Parameter()]
[ValidateSet("ActualCost", "AmortizedCost", "FocusCost", "PriceSheet", "ReservationDetails", "ReservationTransactions", "ReservationRecommendations")]
[ValidateSet("ActualCost", "AmortizedCost", "FocusCost", "PriceSheet", "ReservationDetails", "ReservationRecommendations", "ReservationTransactions")]
[string]
$Dataset = "FocusCost",

Expand All @@ -140,6 +152,20 @@ function New-FinOpsCostExport
[hashtable]
$DatasetFilters,

[Parameter()]
[ValidateSet("Shared", "Single")]
[string]
$CommitmentDiscountScope = "Shared",

[Parameter()]
[string]
$CommitmentDiscountResourceType = "VirtualMachines",

[Parameter()]
[ValidateSet(7, 30, 60)]
[int]
$CommitmentDiscountLookback = 30,

[Parameter(ParameterSetName = "Scheduled")]
[switch]
$Monthly,
Expand Down Expand Up @@ -182,6 +208,10 @@ function New-FinOpsCostExport
[switch]
$DoNotOverwrite,

[Parameter()]
[switch]
$SystemAssignedIdentity,

[Parameter()]
[switch]
$Execute,
Expand Down Expand Up @@ -241,7 +271,6 @@ function New-FinOpsCostExport
timeframe = "Custom"
dataSet = @{
configuration = @{}
granularity = "Daily"
}
}
schedule = @{ status = "Inactive" }
Expand All @@ -257,10 +286,20 @@ function New-FinOpsCostExport
partitionData = (-not $DoNotPartition)
}
}

# Set granularity based on dataset type
if (-not @('PriceSheet', 'ReservationRecommendations', 'ReservationTransactions') -contains $Dataset)
{
$props.properties.definition.dataSet = $props.properties.definition.dataSet | Add-Member -Name granularity -Value 'Daily' -MemberType NoteProperty -Force -PassThru
}

# Enable managed identity
if ($Location)
if ($SystemAssignedIdentity -or $Location)
{
if (-not $Location)
{
$Location = 'global'
}
$props | Add-Member -Name identity -Value @{ type = "SystemAssigned" } -MemberType NoteProperty -Force
$props | Add-Member -Name location -Value $Location -MemberType NoteProperty -Force
}
Expand Down Expand Up @@ -319,6 +358,16 @@ function New-FinOpsCostExport
$props.properties = $props.properties | Add-Member -Name compressionMode -Value "None" -MemberType NoteProperty -Force -PassThru
$props.properties.definition.dataSet.configuration = $props.properties.definition.dataSet.configuration | Add-Member -Name dataVersion -Value $DatasetVersion -MemberType NoteProperty -Force -PassThru
$props.properties.deliveryInfo.destination.type = "AzureBlob"

# Add reservation recommendation filters
if ($Dataset -eq 'ReservationRecommendations')
{
$DatasetFilters = @(
@{ name = 'reservationScope'; value = ($DatasetFilters | Where-Object { $_.name -eq 'reservationScope' }).value ?? $CommitmentDiscountScope }
@{ name = 'resourceType'; value = ($DatasetFilters | Where-Object { $_.name -eq 'resourceType' }).value ?? $CommitmentDiscountResourceType }
@{ name = 'lookBackPeriod'; value = ($DatasetFilters | Where-Object { $_.name -eq 'lookBackPeriod' }).value ?? "Last$($CommitmentDiscountLookback)Days" }
)
}

# Add dataset filters
if ($DatasetFilters.Count -gt 0)
Expand Down
32 changes: 18 additions & 14 deletions src/powershell/Tests/Integration/CostExports.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ Describe 'CostExports' {
# Arrange
$context = Get-AzContext
$rg = "ftk-integration-tests"
$scope = "/subscriptions/$($context.Subscription.Id)/resourceGroups/$rg"
$baScope = "/providers/Microsoft.Billing/billingAccounts/8611537"
$rgScope = "/subscriptions/$($context.Subscription.Id)/resourceGroups/$rg"
$scope = $baScope
$loc = "East US"
$storageName = ([guid]::NewGuid().Guid.Replace('-', '').Substring(0, 24))
$exportName = "ftk-int-CostExports"
Expand All @@ -21,44 +23,46 @@ Describe 'CostExports' {
-SkuName Standard_LRS
}

It 'Should create-read-update-delete exports' {
Monitor "Export tests..." -Indent ' ' {
Monitor "Creating $exportName export..." {
It 'Should create-read-update-delete <_> export' -ForEach @('ActualCost', 'AmortizedCost', 'FocusCost', 'PriceSheet', 'ReservationTransactions', 'ReservationRecommendations', 'ReservationDetails') {
$datasetType = $_
$typedExportName = "$exportName-$datasetType"
Monitor "Export $datasetType tests..." -Indent ' ' {
Monitor "Creating $typedExportName export..." {
# Act -- create
$newResult = New-FinOpsCostExport -Name $exportName -Scope $scope -StorageAccountId $storage.Id -Execute -Backfill 1
$newResult = New-FinOpsCostExport -Name $typedExportName -Scope $scope -Dataset $datasetType -StorageAccountId $storage.Id -Execute -Backfill 1
# TODO: Run tests for all supported API versions: -ApiVersion '2023-08-01'

# Assert
Report -Object $newResult
$newResult.Name | Should -Be $exportName
$newResult.Name | Should -Be $typedExportName
$newResult.RunHistory | Should -BeNullOrEmpty -Because "the -RunHistory option was not specified"
}

Monitor "Getting $exportName..." {
Monitor "Getting $typedExportName..." {
# Act -- read
$getResult = Get-FinOpsCostExport -Name $exportName -Scope $scope -RunHistory
$getResult = Get-FinOpsCostExport -Name $typedExportName -Scope $scope -RunHistory

# Assert
Report "Found $($getResult.Count) export(s)"
Report -Object $getResult
$getResult.Count | Should -Be 1
$getResult.Name | Should -Be $exportName
$getResult.Name | Should -Be $typedExportName
$getResult.RunHistory.Count | Should -BeGreaterThan 0 -Because "-Execute -Backfill 1 was specified during creation"
}

Monitor "Running $exportName..." {
Monitor "Running $typedExportName..." {
# Act -- run now
$runResult = Start-FinOpsCostExport -Name $exportName -Scope $scope
$runResult = Start-FinOpsCostExport -Name $typedExportName -Scope $scope

# Assert
Report $runResult
$runResult | Should -BeTrue
}

Monitor "Deleting $exportName..." {
Monitor "Deleting $typedExportName..." {
# Act -- delete
$deleteResult = Remove-FinOpsCostExport -Name $exportName -Scope $scope
$confirmDeleteResult = Get-FinOpsCostExport -Name $exportName -Scope $scope
$deleteResult = Remove-FinOpsCostExport -Name $typedExportName -Scope $scope
$confirmDeleteResult = Get-FinOpsCostExport -Name $typedExportName -Scope $scope

# Assert
Report $deleteResult
Expand Down

0 comments on commit 4c9b65c

Please sign in to comment.