public/New-SilkTCOCostArray.ps1
|
<# .SYNOPSIS Generates an Azure Resource Cost report. .EXAMPLE generateResourceCostArray -ResourceGroupName MyResourceGroup This generates the cost reports for the RG MyResourceGroup and loads them into a simple date-stamped CSV report. .EXAMPLE generateResourceCostArray -days 28 This generates the cost reports for the current azure subscription context and loads them into a simple date-stamped CSV report for the last 28 days. #> function New-SilkTCOCostArray { param( [Parameter()] [array] $ResourceGroupName, [Parameter()] [int] $days = 1, [Parameter()] [int] $offsetDays = 1 ) $requiredModules = @('Az.CostManagement', 'Az.Resources') foreach ($module in $requiredModules) { if (-not (Get-Module -ListAvailable -Name $module)) { Write-Warning "$module module not found. Installing..." Install-Module -Name $module -Force -AllowClobber } Import-Module $module } $azcontext = get-azcontext if ($ResourceGroupName) { $rg = Get-AzResourceGroup -Name $ResourceGroupName -ErrorAction SilentlyContinue if (!$rg) { Write-Error "Resource group '$ResourceGroupName' not found." exit } $scope = "/subscriptions/$($azcontext.subscription.id)/resourceGroups/$ResourceGroupName" } else { $scope = "/subscriptions/$($azcontext.subscription.id)" } Write-Verbose "Subscription: $($azcontext.subscription.name)" -Verbose Write-Verbose "Resource Group: $ResourceGroupName" -Verbose Write-Verbose "Location: $($rg.Location)" -Verbose # Get that date range $days = ($days + $offsetDays) $Date = Get-Date $startDate = $Date.AddDays(-$days) $endDate = $Date.AddDays(-$offsetDays) $startDateString = $startDate.ToString('yyyy-MM-ddTHH:mm:ssZ') $endDateString = $endDate.ToString('yyyy-MM-ddTHH:mm:ssZ') Write-Verbose "Period: $($startDateString)) to $($endDateString)" -Verbose Write-Verbose "Querying cost data for $($scope) (this may take a moment)..." -Verbose # Create query for cost data using hashtable structure $aggregation = @{ totalCost = @{ name = 'PreTaxCost' function = 'Sum' } } $grouping = @( @{ type = 'Dimension' name = 'ResourceId' }, @{ type = 'Dimension' name = 'ResourceType' }, @{ type = 'Dimension' name = 'MeterCategory' }, @{ type = 'Dimension' name = 'MeterSubCategory' } ) <# Try to include "uptime" for the resources as part of this query. #> # Execute the query $costData = Invoke-AzCostManagementQuery -Scope $scope -Type 'Usage' -Timeframe 'Custom' -TimePeriodFrom $startDateString -TimePeriodTo $endDateString -DatasetGranularity 'None' -DatasetAggregation $aggregation -DatasetGrouping $grouping if (!$costData -or !$costData.Row -or $costData.Row.Count -eq 0) { Write-Verbose "--> No cost data found for scope - $($scope) - in the last $days days." -Verbose Write-Verbose "--> Note: Cost data may have a delay of up to 24-48 hours." -Verbose exit } Write-Verbose "Retrieved $($costData.Row.Count) cost records." -Verbose # Parse the results $columnNames = $costData.Column.Name $report = $costData.Row | ForEach-Object { $row = $_ $rowData = @{} for ($i = 0; $i -lt $columnNames.Count; $i++) { $rowData[$columnNames[$i]] = $row[$i] } $resourceId = $rowData['ResourceId'] $resourceName = if ($resourceId) { ($resourceId -split '/')[-1] } else { 'N/A' } [PSCustomObject] @{ ResourceName = $resourceName ResourceType = $rowData['ResourceType'] MeterCategory = $rowData['MeterCategory'] MeterSubCategory = $rowData['MeterSubCategory'] Cost = [decimal]$rowData['PreTaxCost'] Currency = $rowData['Currency'] ResourceId = $resourceId } } # Filter out zero-cost entries and sort $sortedReport = $report | Where-Object { $_.Cost -gt 0 } | Sort-Object -Property Cost -Descending if ($sortedReport.Count -eq 0) { Write-Error "No resources with costs found in the last {0} days." -f $days exit } # $costByType = $sortedReport | # Group-Object -Property ResourceType | # ForEach-Object { # [PSCustomObject]@{ # ResourceType = $_.Name # Count = $_.Count # TotalCost = ($_.Group | Measure-Object -Property Cost -Sum).Sum # Currency = $_.Group[0].Currency # } # } | # Sort-Object -Property TotalCost -Descending # $costByType return $sortedReport } |