Private/Azure/New-SilkTCOAzureCostArray.ps1

<#
    .SYNOPSIS
    Per resource Azure cost for the VM report.

    .DESCRIPTION
    Reads the shared Cost Details report and rolls it up to one line per resource per
    meter category - one line for the VM, one per disk - which is the shape the merge
    matches on. The window is whole UTC days, so -days 1 is exactly one day of cost.

    .EXAMPLE
    New-SilkTCOAzureCostArray -days 1

    .EXAMPLE
    New-SilkTCOAzureCostArray -ResourceGroupName MyResourceGroup -days 7
#>


function New-SilkTCOAzureCostArray {
    param(
        [Parameter()]
        [array] $ResourceGroupName,
        [Parameter()]
        [int] $days = 1,
        [Parameter()]
        [int] $offsetDays = 1,
        [Parameter()]
        [ValidateSet('AmortizedCost', 'ActualCost')]
        [string] $costMetric = 'AmortizedCost'
    )

    $cd = Get-SilkTCOAzureCostDetails -days $days -offsetDays $offsetDays -metric $costMetric
    if (-not $cd) { return }

    $rgPaths = @($ResourceGroupName | Where-Object { $_ } | ForEach-Object { "/resourcegroups/$("$_".ToLower())/" })

    # one bucket per resource per meter category
    $agg = @{}
    foreach ($r in $cd.Rows) {
        if ($rgPaths) {
            $inScope = $false
            foreach ($p in $rgPaths) { if ($r.ResourceId.Contains($p)) { $inScope = $true; break } }
            if (-not $inScope) { continue }
        }

        $key = "$($r.ResourceId)|$($r.MeterCategory)"
        if (-not $agg.ContainsKey($key)) {
            $agg[$key] = @{ ResourceId = $r.ResourceId; MeterCategory = $r.MeterCategory; Cost = 0.0; Subs = @{} }
        }
        $agg[$key].Cost += $r.Cost
        if ($r.MeterSubCategory) { $agg[$key].Subs[$r.MeterSubCategory] = $true }
    }

    $report = foreach ($a in $agg.Values) {
        if ($a.Cost -le 0) { continue }
        $rid = $a.ResourceId

        $o = New-Object psobject
        $o | Add-Member -MemberType NoteProperty -Name ResourceName -Value $rid.Substring($rid.LastIndexOf('/') + 1)
        $o | Add-Member -MemberType NoteProperty -Name ResourceType -Value (Get-SilkTCOResourceType -resourceId $rid)
        $o | Add-Member -MemberType NoteProperty -Name MeterCategory -Value $a.MeterCategory
        $o | Add-Member -MemberType NoteProperty -Name MeterSubCategory -Value (($a.Subs.Keys | Sort-Object) -join '; ')
        $o | Add-Member -MemberType NoteProperty -Name Cost -Value ([Math]::Round($a.Cost, 4))
        $o | Add-Member -MemberType NoteProperty -Name Currency -Value 'USD'
        $o | Add-Member -MemberType NoteProperty -Name ResourceId -Value $rid
        $o
    }

    if (-not $report) {
        Write-Warning "No Azure resource cost found for $($cd.StartDate) .. $($cd.EndDate)."
        return
    }

    Write-Verbose "Cost lines for $(@($report).Count) resource/category pairs, $($cd.StartDate) .. $($cd.EndDate)." -Verbose
    return ($report | Sort-Object -Property Cost -Descending)
}