From 2ce9a1bfc7ec5c39a12833f3af96d1ff2de2e0f6 Mon Sep 17 00:00:00 2001 From: Paul Frederiksen Date: Sat, 13 Sep 2025 16:30:21 -0700 Subject: [PATCH 1/3] Add aws-cost command for quick AWS cost overview - Add aws-cost function that displays current AWS costs in a formatted table - Shows costs for yesterday, last 7 days, current month, and last month - Displays top 5 services with cost breakdown and percentages - Includes month-over-month cost comparison - Uses AWS Cost Explorer API for accurate cost data - Updated README with aws-cost documentation --- plugins/aws/README.md | 3 + plugins/aws/aws.plugin.zsh | 131 +++++++++++++++++++++++++++++++++++++ 2 files changed, 134 insertions(+) diff --git a/plugins/aws/README.md b/plugins/aws/README.md index 0d0773f63..b0ab22c75 100644 --- a/plugins/aws/README.md +++ b/plugins/aws/README.md @@ -35,6 +35,9 @@ plugins=(... aws) * `aws_change_access_key`: changes the AWS access key of a profile. +* `aws-cost`: displays a quick overview of AWS costs including current month spending, + cost breakdown by service, and comparison with previous periods. + * `aws_profiles`: lists the available profiles in the `$AWS_CONFIG_FILE` (default: `~/.aws/config`). Used to provide completion for the `asp` function. diff --git a/plugins/aws/aws.plugin.zsh b/plugins/aws/aws.plugin.zsh index 0c43031df..15b4b2acb 100644 --- a/plugins/aws/aws.plugin.zsh +++ b/plugins/aws/aws.plugin.zsh @@ -221,6 +221,137 @@ function aws_change_access_key() { aws --no-cli-pager iam list-access-keys } +function aws-cost() { + # Check if AWS CLI is available + if ! command -v aws &> /dev/null; then + echo "${fg[red]}AWS CLI not found. Please install AWS CLI v2.${reset_color}" >&2 + return 1 + fi + + # Get current account ID + local account_id + account_id=$(aws sts get-caller-identity --query Account --output text 2>/dev/null) + if [[ -z "$account_id" ]]; then + echo "${fg[red]}Unable to get AWS account information. Please check your credentials.${reset_color}" >&2 + return 1 + fi + + # Get current region + local region="${AWS_REGION:-${AWS_DEFAULT_REGION:-us-east-1}}" + + echo "AWS Cost Overview - Account: $account_id ($region)" + echo "" + + # Get current date and calculate date ranges + local today=$(date +%Y-%m-%d) + local yesterday=$(date -d "yesterday" +%Y-%m-%d 2>/dev/null || date -v-1d +%Y-%m-%d 2>/dev/null || echo "") + local week_ago=$(date -d "7 days ago" +%Y-%m-%d 2>/dev/null || date -v-7d +%Y-%m-%d 2>/dev/null || echo "") + local month_start=$(date +%Y-%m-01) + local last_month_start=$(date -d "last month" +%Y-%m-01 2>/dev/null || date -v-1m +%Y-%m-01 2>/dev/null || echo "") + local last_month_end=$(date -d "last month" +%Y-%m-%d 2>/dev/null || date -v-1m +%Y-%m-%d 2>/dev/null || echo "") + + # Function to get cost for a date range + local get_cost() { + local start_date="$1" + local end_date="$2" + local granularity="${3:-DAILY}" + + aws ce get-cost-and-usage \ + --time-period Start="$start_date",End="$end_date" \ + --granularity "$granularity" \ + --metrics BlendedCost \ + --query 'ResultsByTime[0].Total.BlendedCost.Amount' \ + --output text 2>/dev/null || echo "0.00" + } + + # Function to get cost by service for current month + local get_cost_by_service() { + aws ce get-cost-and-usage \ + --time-period Start="$month_start",End="$today" \ + --granularity MONTHLY \ + --metrics BlendedCost \ + --group-by Type=DIMENSION,Key=SERVICE \ + --query 'ResultsByTime[0].Groups[?Metrics.BlendedCost.Amount!=`0`] | sort_by(@, &to_number(Metrics.BlendedCost.Amount)) | reverse(@) | [0:5] | [].[Keys[0],Metrics.BlendedCost.Amount]' \ + --output text 2>/dev/null || echo "" + } + + # Get costs for different periods + local yesterday_cost="0.00" + local week_cost="0.00" + local month_cost="0.00" + local last_month_cost="0.00" + + if [[ -n "$yesterday" ]]; then + yesterday_cost=$(get_cost "$yesterday" "$yesterday") + fi + + if [[ -n "$week_ago" ]]; then + week_cost=$(get_cost "$week_ago" "$today") + fi + + month_cost=$(get_cost "$month_start" "$today" "MONTHLY") + + if [[ -n "$last_month_start" && -n "$last_month_end" ]]; then + last_month_cost=$(get_cost "$last_month_start" "$last_month_end" "MONTHLY") + fi + + # Calculate changes + local month_change="0.00" + if [[ "$last_month_cost" != "0.00" && "$month_cost" != "0.00" ]]; then + month_change=$(echo "$month_cost - $last_month_cost" | bc -l 2>/dev/null || echo "0.00") + fi + + # Display cost summary table + echo "📊 CURRENT COSTS" + echo "┌─────────────┬─────────────┬─────────────┐" + echo "│ Period │ Amount │ Change │" + echo "├─────────────┼─────────────┼─────────────┤" + + if [[ -n "$yesterday" ]]; then + printf "│ Yesterday │ $%8s │ - │\n" "$yesterday_cost" + fi + + if [[ -n "$week_ago" ]]; then + printf "│ Last 7 days │ $%8s │ - │\n" "$week_cost" + fi + + printf "│ This month │ $%8s │ $%+8s │\n" "$month_cost" "$month_change" + + if [[ -n "$last_month_start" && -n "$last_month_end" ]]; then + printf "│ Last month │ $%8s │ - │\n" "$last_month_cost" + fi + + echo "└─────────────┴─────────────┴─────────────┘" + echo "" + + # Get and display top services + echo "️ TOP SERVICES (This Month)" + echo "┌─────────────────┬─────────────┬─────────────┐" + echo "│ Service │ Cost │ % of Total │" + echo "├─────────────────┼─────────────┼─────────────┤" + + local services_data + services_data=$(get_cost_by_service) + + if [[ -n "$services_data" ]]; then + local total_month_cost_num=$(echo "$month_cost" | bc -l 2>/dev/null || echo "0") + + echo "$services_data" | while read -r service cost; do + if [[ -n "$service" && -n "$cost" ]]; then + local percentage="0.0" + if [[ "$total_month_cost_num" != "0" ]]; then + percentage=$(echo "scale=1; $cost * 100 / $total_month_cost_num" | bc -l 2>/dev/null || echo "0.0") + fi + printf "│ %-15s │ $%8s │ %6s%% │\n" "$service" "$cost" "$percentage" + fi + done + else + echo "│ No data available │ - │ - │" + fi + + echo "└─────────────────┴─────────────┴─────────────┘" +} + function aws_regions() { local region if [[ $AWS_DEFAULT_REGION ]];then From 913414b3fd8939cb1e71991bc20b7bd8579822e8 Mon Sep 17 00:00:00 2001 From: Paul Frederiksen Date: Sat, 13 Sep 2025 16:41:52 -0700 Subject: [PATCH 2/3] Fix code style issues in aws-cost function - Remove trailing spaces - Fix inconsistent spacing between code blocks - Ensure proper blank line spacing throughout function - Maintain consistency with existing codebase style --- plugins/aws/aws.plugin.zsh | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/plugins/aws/aws.plugin.zsh b/plugins/aws/aws.plugin.zsh index 15b4b2acb..9e8803c8d 100644 --- a/plugins/aws/aws.plugin.zsh +++ b/plugins/aws/aws.plugin.zsh @@ -255,8 +255,8 @@ function aws-cost() { local start_date="$1" local end_date="$2" local granularity="${3:-DAILY}" - - aws ce get-cost-and-usage \ + + aws ce get-cost-and-usage \ --time-period Start="$start_date",End="$end_date" \ --granularity "$granularity" \ --metrics BlendedCost \ @@ -290,7 +290,7 @@ function aws-cost() { fi month_cost=$(get_cost "$month_start" "$today" "MONTHLY") - + if [[ -n "$last_month_start" && -n "$last_month_end" ]]; then last_month_cost=$(get_cost "$last_month_start" "$last_month_end" "MONTHLY") fi @@ -306,21 +306,21 @@ function aws-cost() { echo "┌─────────────┬─────────────┬─────────────┐" echo "│ Period │ Amount │ Change │" echo "├─────────────┼─────────────┼─────────────┤" - + if [[ -n "$yesterday" ]]; then printf "│ Yesterday │ $%8s │ - │\n" "$yesterday_cost" fi - + if [[ -n "$week_ago" ]]; then printf "│ Last 7 days │ $%8s │ - │\n" "$week_cost" fi - + printf "│ This month │ $%8s │ $%+8s │\n" "$month_cost" "$month_change" - + if [[ -n "$last_month_start" && -n "$last_month_end" ]]; then printf "│ Last month │ $%8s │ - │\n" "$last_month_cost" fi - + echo "└─────────────┴─────────────┴─────────────┘" echo "" @@ -332,10 +332,10 @@ function aws-cost() { local services_data services_data=$(get_cost_by_service) - + if [[ -n "$services_data" ]]; then local total_month_cost_num=$(echo "$month_cost" | bc -l 2>/dev/null || echo "0") - + echo "$services_data" | while read -r service cost; do if [[ -n "$service" && -n "$cost" ]]; then local percentage="0.0" @@ -348,7 +348,7 @@ function aws-cost() { else echo "│ No data available │ - │ - │" fi - + echo "└─────────────────┴─────────────┴─────────────┘" } From 3069f2327e60d42bfd43ae194fcd9e88dd378a61 Mon Sep 17 00:00:00 2001 From: Paul Frederiksen Date: Sat, 13 Sep 2025 16:42:29 -0700 Subject: [PATCH 3/3] Rename aws-cost to aws_cost to match existing naming convention - Change function name from aws-cost to aws_cost - Update README documentation to reflect correct function name - Maintains consistency with other AWS plugin functions that use underscores --- plugins/aws/README.md | 2 +- plugins/aws/aws.plugin.zsh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/aws/README.md b/plugins/aws/README.md index b0ab22c75..63a4b4c20 100644 --- a/plugins/aws/README.md +++ b/plugins/aws/README.md @@ -35,7 +35,7 @@ plugins=(... aws) * `aws_change_access_key`: changes the AWS access key of a profile. -* `aws-cost`: displays a quick overview of AWS costs including current month spending, +* `aws_cost`: displays a quick overview of AWS costs including current month spending, cost breakdown by service, and comparison with previous periods. * `aws_profiles`: lists the available profiles in the `$AWS_CONFIG_FILE` (default: `~/.aws/config`). diff --git a/plugins/aws/aws.plugin.zsh b/plugins/aws/aws.plugin.zsh index 9e8803c8d..369634041 100644 --- a/plugins/aws/aws.plugin.zsh +++ b/plugins/aws/aws.plugin.zsh @@ -221,7 +221,7 @@ function aws_change_access_key() { aws --no-cli-pager iam list-access-keys } -function aws-cost() { +function aws_cost() { # Check if AWS CLI is available if ! command -v aws &> /dev/null; then echo "${fg[red]}AWS CLI not found. Please install AWS CLI v2.${reset_color}" >&2