mirror of
https://github.com/Sea-Haven-Industries/seahaven-org-baseline.git
synced 2026-09-30 05:43:17 +00:00
62 lines
4 KiB
Bash
62 lines
4 KiB
Bash
|
|
#!/usr/bin/env bash
|
||
|
|
#
|
||
|
|
# resource-usage-probe.sh — is this resource actually used?
|
||
|
|
#
|
||
|
|
# Run BEFORE acting on an "encrypt / migrate / right-size / encrypt-with-downtime"
|
||
|
|
# finding. Idle resources should usually be retired (cheaper, no downtime) instead
|
||
|
|
# of hardened in place. This is what flipped audit H-19 from "encrypt database-1
|
||
|
|
# with a downtime window" to "snapshot + delete" — it had 0 connections in 60 days.
|
||
|
|
# See feedback memory `feedback_cfn_decommission_and_remediation`.
|
||
|
|
#
|
||
|
|
# Usage:
|
||
|
|
# scripts/resource-usage-probe.sh [--profile NAME] rds <db-instance-id>
|
||
|
|
# scripts/resource-usage-probe.sh [--profile NAME] lambda <function-name>
|
||
|
|
# scripts/resource-usage-probe.sh [--profile NAME] ddb <table-name>
|
||
|
|
# scripts/resource-usage-probe.sh [--profile NAME] ebs <volume-id>
|
||
|
|
#
|
||
|
|
set -euo pipefail
|
||
|
|
PROFILE_ARG=(); ARGS=()
|
||
|
|
while [[ $# -gt 0 ]]; do
|
||
|
|
case "$1" in
|
||
|
|
--profile) PROFILE_ARG=(--profile "$2"); shift 2 ;;
|
||
|
|
-h|--help) sed -n '2,18p' "$0"; exit 0 ;;
|
||
|
|
*) ARGS+=("$1"); shift ;;
|
||
|
|
esac
|
||
|
|
done
|
||
|
|
[[ ${#ARGS[@]} -lt 2 ]] && { echo "usage: $0 [--profile NAME] {rds|lambda|ddb|ebs} <id>" >&2; exit 2; }
|
||
|
|
KIND="${ARGS[0]}"; ID="${ARGS[1]}"; R="us-east-1"
|
||
|
|
aws_() { aws "${PROFILE_ARG[@]}" "$@"; }
|
||
|
|
# UTC window helpers (no Date.now equivalent needed; python gives tz-aware UTC)
|
||
|
|
since() { python3 -c "import datetime;print((datetime.datetime.now(datetime.UTC)-datetime.timedelta(days=$1)).strftime('%Y-%m-%dT%H:%M:%SZ'))"; }
|
||
|
|
now() { python3 -c "import datetime;print(datetime.datetime.now(datetime.UTC).strftime('%Y-%m-%dT%H:%M:%SZ'))"; }
|
||
|
|
maxstat() { aws_ cloudwatch get-metric-statistics --namespace "$1" --metric-name "$2" \
|
||
|
|
--dimensions Name="$3",Value="$4" --start-time "$(since "$5")" --end-time "$(now)" \
|
||
|
|
--period $(( $5 * 86400 )) --statistics Maximum Sum --region "$R" \
|
||
|
|
--query 'Datapoints[0].{Max:Maximum,Sum:Sum}' --output text 2>/dev/null; }
|
||
|
|
|
||
|
|
echo "== $KIND: $ID =="
|
||
|
|
case "$KIND" in
|
||
|
|
rds)
|
||
|
|
aws_ rds describe-db-instances --db-instance-identifier "$ID" --region "$R" \
|
||
|
|
--query 'DBInstances[0].{Engine:Engine,Class:DBInstanceClass,Enc:StorageEncrypted,MultiAZ:MultiAZ,SG:VpcSecurityGroups[].VpcSecurityGroupId}' --output table
|
||
|
|
echo "connections (Max/Sum over 60d): $(maxstat AWS/RDS DatabaseConnections DBInstanceIdentifier "$ID" 60)"
|
||
|
|
echo "tags: $(aws_ rds list-tags-for-resource --resource-name "arn:aws:rds:$R:$(aws_ sts get-caller-identity --query Account --output text):db:$ID" --query 'TagList' --output text 2>/dev/null || echo none)" ;;
|
||
|
|
lambda)
|
||
|
|
echo "invocations (Max/Sum 90d): $(maxstat AWS/Lambda Invocations FunctionName "$ID" 90)"
|
||
|
|
echo "errors (Max/Sum 90d): $(maxstat AWS/Lambda Errors FunctionName "$ID" 90)"
|
||
|
|
aws_ lambda get-function-configuration --function-name "$ID" --region "$R" --query '{LastModified:LastModified,Runtime:Runtime}' --output table 2>/dev/null || true ;;
|
||
|
|
ddb)
|
||
|
|
aws_ dynamodb describe-table --table-name "$ID" --region "$R" --query 'Table.{Items:ItemCount,Bytes:TableSizeBytes,Billing:BillingModeSummary.BillingMode}' --output table
|
||
|
|
echo "consumed write (Max/Sum 30d): $(maxstat AWS/DynamoDB ConsumedWriteCapacityUnits TableName "$ID" 30)"
|
||
|
|
echo "consumed read (Max/Sum 30d): $(maxstat AWS/DynamoDB ConsumedReadCapacityUnits TableName "$ID" 30)"
|
||
|
|
echo "PITR: $(aws_ dynamodb describe-continuous-backups --table-name "$ID" --region "$R" --query 'ContinuousBackupsDescription.PointInTimeRecoveryDescription.PointInTimeRecoveryStatus' --output text 2>/dev/null)" ;;
|
||
|
|
ebs)
|
||
|
|
aws_ ec2 describe-volumes --volume-ids "$ID" --region "$R" \
|
||
|
|
--query 'Volumes[0].{Size:Size,State:State,Enc:Encrypted,Attached:Attachments[0].InstanceId,AttachState:Attachments[0].State}' --output table ;;
|
||
|
|
*) echo "unknown kind: $KIND (use rds|lambda|ddb|ebs)" >&2; exit 2 ;;
|
||
|
|
esac
|
||
|
|
echo
|
||
|
|
echo "VERDICT GUIDE: near-zero connections/invocations/consumed-capacity + no recent attachment => IDLE."
|
||
|
|
echo " IDLE -> retire (final backup, then delete) — cheaper, no downtime than encrypt/migrate-in-place."
|
||
|
|
echo " ACTIVE-> harden in place per the finding."
|