From a156bc44ca079097a2313da86405cd550af17237 Mon Sep 17 00:00:00 2001 From: Steven Noto Date: Fri, 30 Oct 2020 18:20:29 -0500 Subject: [PATCH] Add option for filtering results based on tags. --- README.md | 9 +++++++++ main.go | 53 +++++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 54 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 2acb23b..65d7913 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,7 @@ GLOBAL OPTIONS: --end value Second month to compare (2020-02-01) (default: "2020-10-01") --cost-metric value Cost Metric to compare (NetAmortizedCost, UnblendedCost, etc.) (default: "NetAmortizedCost") --service value Define a service to dig into + --tag value Tag value to filter results (app=web, env=prod, etc.) --sort value Column to sort results on (name, start, end, delta) (default: "name") --sort-order value Order to sort in (asc or desc) (default: "asc") --help, -h show help (default: false) @@ -61,11 +62,19 @@ aws-cct --cost-metric UnblendedCost ``` *Dig into EC2 costs* + You can get the string from the initial output. Simply copy the value in the "SERVICE" section and you can filter into that ``` aws-cct --service "Amazon Elastic Compute Cloud - Compute" ``` +*Filter by tags* + +You can get filter costs by tag, to return costs for resources that match all specified tag values. +``` +aws-cct --tag app=widgetizer --tag env=production +``` + *Compare Older Months* ``` aws-cct --start 2020-08-01 --end 2020-09-01 diff --git a/main.go b/main.go index 247299f..6269b1a 100644 --- a/main.go +++ b/main.go @@ -59,6 +59,10 @@ func main() { Usage: "Define a service to dig into", Destination: &serviceFilter, }, + &cli.StringSliceFlag{ + Name: "tag", + Usage: "Tag value to filter results (app=web, env=prod, etc.)", + }, &cli.StringFlag{ Name: "sort", Value: "name", @@ -100,8 +104,10 @@ func main() { grouping = "USAGE_TYPE" } - firstResultsCosts := GetCosts(svc, firstMonthStart, firstMonthEnd, costMetric, grouping, serviceFilter) - secondResultsCosts := GetCosts(svc, secondMonthStart, secondMonthEnd, costMetric, grouping, serviceFilter) + tagFilters := c.StringSlice("tag") + + firstResultsCosts := GetCosts(svc, firstMonthStart, firstMonthEnd, costMetric, grouping, serviceFilter, tagFilters) + secondResultsCosts := GetCosts(svc, secondMonthStart, secondMonthEnd, costMetric, grouping, serviceFilter, tagFilters) allServiceNames := ExtractAllServiceNames(firstResultsCosts, secondResultsCosts) type ServiceCosts struct { @@ -198,16 +204,29 @@ func ExtractAllServiceNames(firstResultsCosts map[string]float64, secondResultsC return allServiceNames } -func GetCosts(svc *costexplorer.CostExplorer, start string, end string, costmetric string, grouping string, serviceFilter string) map[string]float64 { - var filter *costexplorer.Expression +func GetCosts(svc *costexplorer.CostExplorer, start string, end string, costmetric string, grouping string, serviceFilter string, tagFilters []string) map[string]float64 { + // Assemble filters + var filters []*costexplorer.Expression + if len(tagFilters) > 0 { + for _, tagFilter := range tagFilters { + tagParts := strings.SplitN(tagFilter, "=", 2) + if len(tagParts) == 2 { + filters = append(filters, GetTagExpression(tagParts[0], tagParts[1])) + } + } + } if serviceFilter != "" { + filters = append(filters, GetDimensionExpression("SERVICE", serviceFilter)) + } + var filter *costexplorer.Expression + if len(filters) > 1 { filter = &costexplorer.Expression{ - Dimensions: &costexplorer.DimensionValues{ - Key: aws.String("SERVICE"), - Values: aws.StringSlice([]string{serviceFilter}), - }, + And: filters, } + } else if len(filters) == 1 { + filter = filters[0] } + costInput := &costexplorer.GetCostAndUsageInput{ Filter: filter, Granularity: aws.String("MONTHLY"), @@ -248,3 +267,21 @@ func GetCosts(svc *costexplorer.CostExplorer, start string, end string, costmetr } return resultsCosts } + +func GetTagExpression(tag string, value string) *costexplorer.Expression { + return &costexplorer.Expression{ + Tags: &costexplorer.TagValues{ + Key: aws.String(tag), + Values: aws.StringSlice([]string{value}), + }, + } +} + +func GetDimensionExpression(dimension string, value string) *costexplorer.Expression { + return &costexplorer.Expression{ + Dimensions: &costexplorer.DimensionValues{ + Key: aws.String(dimension), + Values: aws.StringSlice([]string{value}), + }, + } +}