Last active
March 5, 2026 05:50
-
-
Save adamcousins/cf50025d658efc5adb2ea18f5337a842 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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