Skip to content

Instantly share code, notes, and snippets.

@adamcousins
Last active March 5, 2026 05:50
Show Gist options
  • Select an option

  • Save adamcousins/cf50025d658efc5adb2ea18f5337a842 to your computer and use it in GitHub Desktop.

Select an option

Save adamcousins/cf50025d658efc5adb2ea18f5337a842 to your computer and use it in GitHub Desktop.
#!/bin/bash
#
# ELB Logging Report Script
#
# This script generates a report of all ELB logging configurations
# across all AWS Organization accounts.
#
# Usage:
# ./elb_logging_report.sh [--region REGION] [--output FILE]
#
# Examples:
# ./elb_logging_report.sh
# ./elb_logging_report.sh --region us-east-1
# ./elb_logging_report.sh --output elb-report.csv
#
# Don't exit on errors, we want to continue through all accounts
set -o pipefail
# Default values
REGION="ap-southeast-2"
OUTPUT_FILE="elb-logging-report.csv"
ROLE_NAME="OrganizationAccountAccessRole"
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
# Parse arguments
while [[ $# -gt 0 ]]; do
case $1 in
--region)
REGION="$2"
shift 2
;;
--output)
OUTPUT_FILE="$2"
shift 2
;;
*)
echo -e "${RED}Unknown option: $1${NC}"
exit 1
;;
esac
done
# Check dependencies
if ! command -v aws &> /dev/null; then
echo -e "${RED}Error: AWS CLI is required${NC}"
exit 1
fi
if ! command -v jq &> /dev/null; then
echo -e "${RED}Error: jq is required${NC}"
exit 1
fi
echo -e "${GREEN}========================================${NC}"
echo -e "${GREEN}ELB Logging Configuration Report${NC}"
echo -e "${GREEN}========================================${NC}"
echo ""
# Show current AWS identity
CALLER_IDENTITY=$(aws sts get-caller-identity --output json)
MGMT_ACCOUNT_ID=$(echo "$CALLER_IDENTITY" | jq -r '.Account')
ARN=$(echo "$CALLER_IDENTITY" | jq -r '.Arn')
echo -e "Management Account: ${YELLOW}${MGMT_ACCOUNT_ID}${NC}"
echo -e "ARN: ${YELLOW}${ARN}${NC}"
echo -e "Region: ${YELLOW}${REGION}${NC}"
echo -e "Output: ${YELLOW}${OUTPUT_FILE}${NC}"
echo ""
# Initialize CSV file
cat > "$OUTPUT_FILE" << 'EOF'
AccountID,AccountName,Region,LoadBalancerName,LoadBalancerType,LoggingEnabled,S3Bucket,S3Prefix,LoadBalancerArn
EOF
# Counters
TOTAL_ACCOUNTS=0
TOTAL_ELBS=0
TOTAL_WITH_LOGGING=0
# Function to assume role
assume_role() {
local account_id="$1"
local role_arn="arn:aws:iam::${account_id}:role/${ROLE_NAME}"
echo -e " Assuming role: $role_arn"
local OUT
if ! OUT=$(aws sts assume-role \
--role-arn "$role_arn" \
--role-session-name "elb-report-${account_id}" \
--region "$REGION" \
--output json 2>&1); then
echo -e " ${RED}Failed to assume role: $OUT${NC}"
return 1
fi
export AWS_ACCESS_KEY_ID=$(echo "$OUT" | jq -r '.Credentials.AccessKeyId')
export AWS_SECRET_ACCESS_KEY=$(echo "$OUT" | jq -r '.Credentials.SecretAccessKey')
export AWS_SESSION_TOKEN=$(echo "$OUT" | jq -r '.Credentials.SessionToken')
echo -e " ${GREEN}✓ Successfully assumed role${NC}"
return 0
}
# Function to unset credentials
unset_credentials() {
unset AWS_ACCESS_KEY_ID
unset AWS_SECRET_ACCESS_KEY
unset AWS_SESSION_TOKEN
}
# Function to get ELB type from ARN
get_elb_type_from_arn() {
local arn="$1"
echo "$arn" | cut -d'/' -f2
}
# Function to process Classic ELBs
process_classic_elbs() {
local account_id="$1"
local account_name="$2"
echo -e "\n Checking Classic ELBs..."
local classic_lbs
if ! classic_lbs=$(aws elb describe-load-balancers --region "$REGION" --output json 2>&1); then
echo -e " ${YELLOW}⚠ Could not list Classic ELBs: $classic_lbs${NC}"
return 0
fi
local lb_count=$(echo "$classic_lbs" | jq -r '.LoadBalancerDescriptions | length' 2>/dev/null || echo "0")
if [ "$lb_count" -eq 0 ]; then
echo -e " ${BLUE}No Classic ELBs found${NC}"
return 0
fi
echo -e " ${BLUE}Found $lb_count Classic ELB(s)${NC}"
local lb_names
lb_names=$(echo "$classic_lbs" | jq -r '.LoadBalancerDescriptions[].LoadBalancerName' 2>/dev/null)
while IFS= read -r lb_name; do
[ -z "$lb_name" ] && continue
# Get attributes
local attrs
if ! attrs=$(aws elb describe-load-balancer-attributes \
--load-balancer-name "$lb_name" \
--region "$REGION" \
--output json 2>&1); then
echo -e " ${YELLOW}⚠ Could not get attributes for $lb_name${NC}"
continue
fi
local enabled=$(echo "$attrs" | jq -r '.LoadBalancerAttributes.AccessLog.Enabled // false' 2>/dev/null || echo "false")
local bucket=$(echo "$attrs" | jq -r '.LoadBalancerAttributes.AccessLog.S3BucketName // ""' 2>/dev/null || echo "")
local prefix=$(echo "$attrs" | jq -r '.LoadBalancerAttributes.AccessLog.S3BucketPrefix // ""' 2>/dev/null || echo "")
# Write to CSV
echo "$account_id,$account_name,$REGION,$lb_name,classic,$enabled,$bucket,$prefix,N/A" >> "$OUTPUT_FILE"
if [ "$enabled" = "true" ]; then
echo -e " ${GREEN}✓${NC} $lb_name - Logging: ${GREEN}ENABLED${NC} → s3://${bucket}/${prefix}"
else
echo -e " ${YELLOW}○${NC} $lb_name - Logging: ${YELLOW}DISABLED${NC}"
fi
done <<< "$lb_names"
}
# Function to process ALB/NLB
process_elbv2() {
local account_id="$1"
local account_name="$2"
local elbv2_lbs
elbv2_lbs=$(aws elbv2 describe-load-balancers --region "$REGION" --output json 2>/dev/null || echo '{"LoadBalancers":[]}')
local lb_data
lb_data=$(echo "$elbv2_lbs" | jq -c '.LoadBalancers[]' 2>/dev/null || echo "")
if [ -z "$lb_data" ]; then
return 0
fi
while read -r lb; do
[ -z "$lb" ] && continue
local lb_arn=$(echo "$lb" | jq -r '.LoadBalancerArn')
local lb_name=$(echo "$lb" | jq -r '.LoadBalancerName')
local elb_type=$(get_elb_type_from_arn "$lb_arn")
# Get attributes
local attrs
attrs=$(aws elbv2 describe-load-balancer-attributes \
--load-balancer-arn "$lb_arn" \
--region "$REGION" \
--output json 2>/dev/null || echo '{"Attributes":[]}')
local enabled=$(echo "$attrs" | jq -r '.Attributes[] | select(.Key=="access_logs.s3.enabled") | .Value // "false"')
local bucket=$(echo "$attrs" | jq -r '.Attributes[] | select(.Key=="access_logs.s3.bucket") | .Value // ""')
local prefix=$(echo "$attrs" | jq -r '.Attributes[] | select(.Key=="access_logs.s3.prefix") | .Value // ""')
# Write to CSV (escape commas in ARN)
local escaped_arn=$(echo "$lb_arn" | sed 's/,/;/g')
echo "$account_id,$account_name,$REGION,$lb_name,$elb_type,$enabled,$bucket,$prefix,$escaped_arn" >> "$OUTPUT_FILE"
if [ "$enabled" = "true" ]; then
echo -e " ${GREEN}✓${NC} $lb_name (${elb_type^^}) - Logging: ${GREEN}ENABLED${NC} → s3://${bucket}/${prefix}"
else
echo -e " ${YELLOW}○${NC} $lb_name (${elb_type^^}) - Logging: ${YELLOW}DISABLED${NC}"
fi
done <<< "$lb_data"
}
# Function to process a single account
process_account() {
local account_id="$1"
local account_name="$2"
echo -e "\n${GREEN}========================================${NC}"
echo -e "${GREEN}Account: $account_name ($account_id)${NC}"
echo -e "${GREEN}========================================${NC}"
((TOTAL_ACCOUNTS++)) || true
# Save original credentials
local orig_access_key="${AWS_ACCESS_KEY_ID:-}"
local orig_secret_key="${AWS_SECRET_ACCESS_KEY:-}"
local orig_session_token="${AWS_SESSION_TOKEN:-}"
# Assume role in target account
if ! assume_role "$account_id"; then
echo -e "${YELLOW}⚠ Could not assume role in account $account_id - skipping${NC}"
# Restore credentials even on failure
unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN
[ -n "$orig_access_key" ] && export AWS_ACCESS_KEY_ID="$orig_access_key"
[ -n "$orig_secret_key" ] && export AWS_SECRET_ACCESS_KEY="$orig_secret_key"
[ -n "$orig_session_token" ] && export AWS_SESSION_TOKEN="$orig_session_token"
return 0
fi
# Process Classic ELBs
process_classic_elbs "$account_id" "$account_name" || true
# Process ALB/NLB
process_elbv2 "$account_id" "$account_name" || true
# Always restore original credentials
unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN
[ -n "$orig_access_key" ] && export AWS_ACCESS_KEY_ID="$orig_access_key"
[ -n "$orig_secret_key" ] && export AWS_SECRET_ACCESS_KEY="$orig_secret_key"
[ -n "$orig_session_token" ] && export AWS_SESSION_TOKEN="$orig_session_token"
echo -e "${GREEN}✓ Completed account $account_name${NC}"
}
# Main execution
echo -e "${GREEN}Fetching organization accounts...${NC}"
# Get all active accounts
if ! accounts=$(aws organizations list-accounts --output json 2>&1); then
echo -e "${RED}Error: Could not list organization accounts${NC}"
echo "$accounts"
exit 1
fi
active_accounts=$(echo "$accounts" | jq -r '.Accounts[] | select(.Status=="ACTIVE") | "\(.Id)|\(.Name)"')
account_count=$(echo "$active_accounts" | wc -l | tr -d ' ')
echo -e "Found ${YELLOW}${account_count}${NC} active accounts\n"
# Process each account
while IFS='|' read -r account_id account_name; do
[ -z "$account_id" ] && continue
process_account "$account_id" "$account_name" || {
echo -e "${RED}Error processing account $account_id, continuing...${NC}"
continue
}
done <<< "$active_accounts"
# Summary
echo -e "\n${GREEN}========================================${NC}"
echo -e "${GREEN}SUMMARY${NC}"
echo -e "${GREEN}========================================${NC}"
echo -e "Total accounts processed: ${YELLOW}${TOTAL_ACCOUNTS}${NC}"
# Count results from CSV
TOTAL_ELBS=$(tail -n +2 "$OUTPUT_FILE" | wc -l)
TOTAL_WITH_LOGGING=$(tail -n +2 "$OUTPUT_FILE" | grep -c ',true,' || echo "0")
echo -e "Total load balancers found: ${YELLOW}${TOTAL_ELBS}${NC}"
echo -e "Load balancers with logging: ${YELLOW}${TOTAL_WITH_LOGGING}${NC}"
echo -e "\nReport saved to: ${YELLOW}${OUTPUT_FILE}${NC}"
echo ""
# Show sample of results
echo -e "${GREEN}Sample results:${NC}"
head -6 "$OUTPUT_FILE" | column -t -s','
echo -e "\n${GREEN}To view full report:${NC}"
echo -e " cat $OUTPUT_FILE"
echo -e " column -t -s',' $OUTPUT_FILE | less -S"
echo ""
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment