#!/usr/bin/env bash
set -uo pipefail
export AWS_PAGER=""

# AWS Security Baseline - READ-ONLY inventory
# ------------------------------------------------------------
# Purpose:
#   Show which key AWS security/visibility services are already enabled.
#   This script makes LIST/DESCRIBE/GET calls only. It DOES NOT enable,
#   disable, create, update, or delete anything.
#
# Default:
#   Scan every AWS Region that is opted-in or enabled for this account.
#
# Optional:
#   SCAN_ALL_REGIONS=0 REGION=eu-west-1 ./aws-security-enabled-audit.sh
#
# Output:
#   ~/aws-security-enabled-audit-YYYYmmdd-HHMMSS.md
#   ~/aws-security-enabled-audit-YYYYmmdd-HHMMSS.txt

SCAN_ALL_REGIONS="${SCAN_ALL_REGIONS:-1}"
REGION="${REGION:-${AWS_REGION:-${AWS_DEFAULT_REGION:-eu-west-1}}}"
TS="$(date -u +%Y%m%d-%H%M%S)"
REPORT_MD="$HOME/aws-security-enabled-audit-${TS}.md"
REPORT_TXT="$HOME/aws-security-enabled-audit-${TS}.txt"
TMP_DIR="$(mktemp -d)"
trap 'rm -rf "$TMP_DIR"' EXIT

GREEN="✅"
RED="❌"
WARN="⚠️"
INFO="ℹ️"

have() { command -v "$1" >/dev/null 2>&1; }

if ! have aws; then
  echo "ERROR: AWS CLI is required."
  exit 1
fi

ACCOUNT_ID="$(aws sts get-caller-identity --query Account --output text 2>/dev/null || true)"
CALLER_ARN="$(aws sts get-caller-identity --query Arn --output text 2>/dev/null || true)"
if [[ -z "$ACCOUNT_ID" || "$ACCOUNT_ID" == "None" ]]; then
  echo "ERROR: Could not determine the current AWS account. Check CloudShell credentials."
  exit 1
fi

if [[ "$SCAN_ALL_REGIONS" == "1" ]]; then
  REGIONS="$(aws ec2 describe-regions \
    --all-regions \
    --query "Regions[?OptInStatus=='opt-in-not-required'||OptInStatus=='opted-in'].RegionName" \
    --output text 2>/dev/null || true)"
  if [[ -z "$REGIONS" ]]; then
    REGIONS="$REGION"
  fi
else
  REGIONS="$REGION"
fi

REGION_COUNT="$(wc -w <<<"$REGIONS" | tr -d ' ')"

# ---------- helpers ----------

append_md() { printf '%s\n' "$*" >> "$REPORT_MD"; }
append_txt() { printf '%s\n' "$*" >> "$REPORT_TXT"; }

both() {
  echo "$*"
  append_txt "$*"
}

heading() {
  echo
  echo "============================================================"
  echo "$1"
  echo "============================================================"
  append_txt ""
  append_txt "============================================================"
  append_txt "$1"
  append_txt "============================================================"
  append_md ""
  append_md "## $1"
  append_md ""
}

subheading() {
  echo
  echo "--- $1 ---"
  append_txt ""
  append_txt "--- $1 ---"
  append_md ""
  append_md "### $1"
  append_md ""
}

status_line() {
  local icon="$1"; shift
  local label="$1"; shift
  local detail="$*"
  printf '%-3s %-30s %s\n' "$icon" "$label" "$detail"
  printf '%-3s %-30s %s\n' "$icon" "$label" "$detail" >> "$REPORT_TXT"
  append_md "- ${icon} **${label}:** ${detail}"
}

capture() {
  # capture <outfile> <aws args...>
  local outfile="$1"; shift
  aws "$@" >"$outfile" 2>"${outfile}.err"
  return $?
}

json_len() {
  # json_len <file> <python-expression returning list>
  python3 - "$1" "$2" <<'PY' 2>/dev/null
import json,sys
p,expr=sys.argv[1:3]
d=json.load(open(p))
try:
    v=eval(expr, {"__builtins__":{}}, {"d":d})
    print(len(v or []))
except Exception:
    print(0)
PY
}

json_get() {
  # json_get <file> <python-expression>
  python3 - "$1" "$2" <<'PY' 2>/dev/null
import json,sys
p,expr=sys.argv[1:3]
d=json.load(open(p))
try:
    v=eval(expr, {"__builtins__":{}}, {"d":d})
    if v is None:
        print("")
    elif isinstance(v,(dict,list)):
        print(json.dumps(v,separators=(",",":")))
    else:
        print(v)
except Exception:
    print("")
PY
}

classify_error() {
  local errfile="$1"
  local s
  s="$(cat "$errfile" 2>/dev/null || true)"
  if grep -Eqi 'AccessDenied|Unauthorized|not authorized|permission' <<<"$s"; then
    echo "PERMISSION"
  elif grep -Eqi 'not subscribed|not enabled|ResourceNotFound|DetectorNotFound|SubscriptionException|not found' <<<"$s"; then
    echo "NOT_ENABLED"
  elif grep -Eqi 'Could not connect to the endpoint|Invalid endpoint|UnknownOperation|UnsupportedOperation|not supported' <<<"$s"; then
    echo "UNSUPPORTED"
  else
    echo "ERROR"
  fi
}

# ---------- report headers ----------

cat >"$REPORT_MD" <<EOF
# AWS Security Enabled-State Audit

- **Generated (UTC):** $(date -u '+%Y-%m-%d %H:%M:%S')
- **AWS account:** ${ACCOUNT_ID}
- **Caller:** ${CALLER_ARN}
- **Regions scanned:** ${REGION_COUNT}
- **Mode:** READ ONLY - no AWS configuration changes are made.

EOF

cat >"$REPORT_TXT" <<EOF
AWS SECURITY ENABLED-STATE AUDIT
Generated (UTC): $(date -u '+%Y-%m-%d %H:%M:%S')
AWS account: ${ACCOUNT_ID}
Caller: ${CALLER_ARN}
Regions scanned: ${REGION_COUNT}
Mode: READ ONLY - no AWS configuration changes are made.
EOF

echo
echo "AWS Security Enabled-State Audit"
echo "Account: $ACCOUNT_ID"
echo "Caller : $CALLER_ARN"
echo "Regions: $REGION_COUNT"
echo
echo "READ-ONLY: this script does not enable or change anything."

# ============================================================
# GLOBAL / ACCOUNT SECURITY
# ============================================================

heading "Account-level security"

# IAM account summary (root MFA/access keys)
f="$TMP_DIR/iam-summary.json"
if capture "$f" iam get-account-summary; then
  ROOT_MFA="$(json_get "$f" "d.get('SummaryMap',{}).get('AccountMFAEnabled',0)")"
  ROOT_KEYS="$(json_get "$f" "d.get('SummaryMap',{}).get('AccountAccessKeysPresent',0)")"
  USERS="$(json_get "$f" "d.get('SummaryMap',{}).get('Users',0)")"
  ROLES="$(json_get "$f" "d.get('SummaryMap',{}).get('Roles',0)")"
  [[ "$ROOT_MFA" == "1" ]] && status_line "$GREEN" "Root MFA" "Enabled" || status_line "$RED" "Root MFA" "Not detected as enabled"
  [[ "$ROOT_KEYS" == "0" ]] && status_line "$GREEN" "Root access keys" "None present" || status_line "$WARN" "Root access keys" "Present"
  status_line "$INFO" "IAM inventory" "${USERS:-?} users, ${ROLES:-?} roles"
else
  status_line "$WARN" "IAM account summary" "Unable to read ($(classify_error "${f}.err"))"
fi

# S3 account Public Access Block
f="$TMP_DIR/s3-pab.json"
if capture "$f" s3control get-public-access-block --account-id "$ACCOUNT_ID" --region us-east-1; then
  PAB="$(json_get "$f" "d.get('PublicAccessBlockConfiguration',{})")"
  status_line "$GREEN" "S3 account Public Access Block" "$PAB"
else
  status_line "$WARN" "S3 account Public Access Block" "Not returned / unavailable ($(classify_error "${f}.err"))"
fi

# Shield Advanced
f="$TMP_DIR/shield.json"
if capture "$f" shield describe-subscription --region us-east-1; then
  AUTO="$(json_get "$f" "d.get('Subscription',{}).get('AutoRenew','UNKNOWN')")"
  status_line "$GREEN" "AWS Shield Advanced" "Subscribed (AutoRenew=${AUTO})"
else
  ec="$(classify_error "${f}.err")"
  if [[ "$ec" == "NOT_ENABLED" ]]; then
    status_line "$RED" "AWS Shield Advanced" "Not subscribed"
  else
    status_line "$WARN" "AWS Shield Advanced" "Unable to determine (${ec})"
  fi
fi

# CloudFront distributions + standard logging/WAF association
f="$TMP_DIR/cloudfront.json"
if capture "$f" cloudfront list-distributions; then
  python3 - "$f" "$REPORT_MD" "$REPORT_TXT" <<'PY'
import json,sys
p,md,txt=sys.argv[1:4]
d=json.load(open(p))
items=((d.get("DistributionList") or {}).get("Items") or [])
enabled=sum(1 for x in items if (x.get("Logging") or {}).get("Enabled"))
waf=sum(1 for x in items if x.get("WebACLId"))
line=f"{len(items)} distributions; standard logging enabled on {enabled}; WAF associated on {waf}"
print(f"ℹ️  {'CloudFront':30} {line}")
with open(txt,"a") as f: f.write(f"ℹ️  {'CloudFront':30} {line}\n")
with open(md,"a") as f: f.write(f"- ℹ️ **CloudFront:** {line}\n")
PY
else
  status_line "$WARN" "CloudFront" "Unable to read ($(classify_error "${f}.err"))"
fi

# Global WAF + logging
subheading "CloudFront AWS WAF (global)"
f="$TMP_DIR/waf-global.json"
if capture "$f" wafv2 list-web-acls --scope CLOUDFRONT --region us-east-1; then
  WAF_COUNT="$(json_len "$f" "d.get('WebACLs',[])")"
  status_line "$GREEN" "CloudFront Web ACLs" "${WAF_COUNT} found"
  python3 - "$f" <<'PY' >"$TMP_DIR/waf-arns.txt"
import json,sys
for w in json.load(open(sys.argv[1])).get("WebACLs",[]):
    print((w.get("Name") or "")+"\t"+(w.get("ARN") or ""))
PY
  while IFS=$'\t' read -r name arn; do
    [[ -z "$arn" ]] && continue
    lf="$TMP_DIR/waf-log-$(echo "$name" | tr -cd '[:alnum:]_-').json"
    if capture "$lf" wafv2 get-logging-configuration --resource-arn "$arn" --region us-east-1; then
      dest="$(json_get "$lf" "d.get('LoggingConfiguration',{}).get('LogDestinationConfigs',[])")"
      status_line "$GREEN" "WAF logging: $name" "$dest"
    else
      ec="$(classify_error "${lf}.err")"
      if [[ "$ec" == "NOT_ENABLED" ]]; then
        status_line "$RED" "WAF logging: $name" "Not configured"
      else
        status_line "$WARN" "WAF logging: $name" "Unable to determine (${ec})"
      fi
    fi
  done <"$TMP_DIR/waf-arns.txt"
else
  status_line "$WARN" "CloudFront Web ACLs" "Unable to read ($(classify_error "${f}.err"))"
fi

# ============================================================
# REGIONAL SECURITY SERVICES
# ============================================================

heading "Regional security services"

append_md "| Region | GuardDuty | Security Hub | Inspector | Security Lake | AWS Config | Access Analyzer | Macie | Detective | EBS Encryption |"
append_md "|---|---|---|---|---|---|---|---|---|---|---|"

for r in $REGIONS; do
  echo
  echo "Scanning region: $r"

  gd="Unknown"; sh="Unknown"; ins="Unknown"; sl="Unknown"; cfg="Unknown"
  aa="Unknown"; mac="Unknown"; det="Unknown"; ebs="Unknown"

  # GuardDuty
  f="$TMP_DIR/gd-$r.json"
  if capture "$f" guardduty list-detectors --region "$r"; then
    detector="$(json_get "$f" "(d.get('DetectorIds') or [''])[0] if d.get('DetectorIds') else ''")"
    if [[ -n "$detector" ]]; then
      gf="$TMP_DIR/gd-get-$r.json"
      if capture "$gf" guardduty get-detector --detector-id "$detector" --region "$r"; then
        stat="$(json_get "$gf" "d.get('Status','UNKNOWN')")"
        gd="$stat"
      else
        gd="ERROR"
      fi
    else
      gd="Disabled"
    fi
  else
    ec="$(classify_error "${f}.err")"
    [[ "$ec" == "PERMISSION" ]] && gd="No permission" || gd="Unavailable"
  fi

  # Security Hub
  f="$TMP_DIR/sh-$r.json"
  if capture "$f" securityhub describe-hub --region "$r"; then
    standardsf="$TMP_DIR/sh-standards-$r.json"
    nstd="?"
    if capture "$standardsf" securityhub get-enabled-standards --region "$r"; then
      nstd="$(json_len "$standardsf" "d.get('StandardsSubscriptions',[])")"
    fi
    sh="Enabled (${nstd} standards)"
  else
    ec="$(classify_error "${f}.err")"
    [[ "$ec" == "NOT_ENABLED" ]] && sh="Disabled" || [[ "$ec" == "PERMISSION" ]] && sh="No permission" || sh="Unavailable"
  fi

  # Inspector v2
  f="$TMP_DIR/ins-$r.json"
  if capture "$f" inspector2 batch-get-account-status --account-ids "$ACCOUNT_ID" --region "$r"; then
    stat="$(json_get "$f" "(d.get('accounts') or [{}])[0].get('state',{}).get('status','UNKNOWN') if d.get('accounts') else 'DISABLED'")"
    resources="$(json_get "$f" "(d.get('accounts') or [{}])[0].get('resourceState',{}) if d.get('accounts') else {}")"
    if [[ "$stat" == "ENABLED" ]]; then
      ins="Enabled"
    elif [[ "$stat" == "DISABLED" ]]; then
      ins="Disabled"
    else
      ins="$stat"
    fi
    echo "$resources" >"$TMP_DIR/ins-res-$r.txt"
  else
    ec="$(classify_error "${f}.err")"
    [[ "$ec" == "PERMISSION" ]] && ins="No permission" || ins="Unavailable"
  fi

  # Security Lake
  f="$TMP_DIR/sl-$r.json"
  if capture "$f" securitylake list-data-lakes --regions "$r" --region "$r"; then
    cnt="$(json_len "$f" "d.get('dataLakes',[])")"
    if [[ "$cnt" -gt 0 ]]; then
      stat="$(json_get "$f" "(d.get('dataLakes') or [{}])[0].get('createStatus','UNKNOWN')")"
      sl="Enabled (${stat})"
    else
      sl="Disabled"
    fi
  else
    ec="$(classify_error "${f}.err")"
    [[ "$ec" == "PERMISSION" ]] && sl="No permission" || sl="Unavailable"
  fi

  # AWS Config
  f="$TMP_DIR/cfg-$r.json"
  if capture "$f" configservice describe-configuration-recorder-status --region "$r"; then
    cnt="$(json_len "$f" "d.get('ConfigurationRecordersStatus',[])")"
    if [[ "$cnt" -gt 0 ]]; then
      rec="$(json_get "$f" "any(x.get('recording') for x in d.get('ConfigurationRecordersStatus',[]))")"
      [[ "$rec" == "True" ]] && cfg="Recording" || cfg="Recorder stopped"
    else
      cfg="Disabled"
    fi
  else
    ec="$(classify_error "${f}.err")"
    [[ "$ec" == "PERMISSION" ]] && cfg="No permission" || cfg="Unavailable"
  fi

  # IAM Access Analyzer
  f="$TMP_DIR/aa-$r.json"
  if capture "$f" accessanalyzer list-analyzers --region "$r"; then
    cnt="$(json_len "$f" "d.get('analyzers',[])")"
    active="$(python3 - "$f" <<'PY' 2>/dev/null
import json,sys
d=json.load(open(sys.argv[1]))
print(sum(1 for x in d.get("analyzers",[]) if x.get("status")=="ACTIVE"))
PY
)"
    [[ "$cnt" -gt 0 ]] && aa="Enabled (${active}/${cnt} active)" || aa="Disabled"
  else
    ec="$(classify_error "${f}.err")"
    [[ "$ec" == "PERMISSION" ]] && aa="No permission" || aa="Unavailable"
  fi

  # Macie
  f="$TMP_DIR/macie-$r.json"
  if capture "$f" macie2 get-macie-session --region "$r"; then
    stat="$(json_get "$f" "d.get('status','UNKNOWN')")"
    mac="$stat"
  else
    ec="$(classify_error "${f}.err")"
    [[ "$ec" == "NOT_ENABLED" ]] && mac="Disabled" || [[ "$ec" == "PERMISSION" ]] && mac="No permission" || mac="Unavailable"
  fi

  # Detective
  f="$TMP_DIR/det-$r.json"
  if capture "$f" detective list-graphs --region "$r"; then
    cnt="$(json_len "$f" "d.get('GraphList',[])")"
    [[ "$cnt" -gt 0 ]] && det="Enabled (${cnt} graph)" || det="Disabled"
  else
    ec="$(classify_error "${f}.err")"
    [[ "$ec" == "PERMISSION" ]] && det="No permission" || det="Unavailable"
  fi

  # EBS encryption by default
  f="$TMP_DIR/ebs-$r.json"
  if capture "$f" ec2 get-ebs-encryption-by-default --region "$r"; then
    flag="$(json_get "$f" "d.get('EbsEncryptionByDefault',False)")"
    [[ "$flag" == "True" ]] && ebs="Enabled" || ebs="Disabled"
  else
    ebs="Unavailable"
  fi

  # Print concise region line
  echo "  GuardDuty      : $gd"
  echo "  Security Hub   : $sh"
  echo "  Inspector      : $ins"
  echo "  Security Lake  : $sl"
  echo "  AWS Config     : $cfg"
  echo "  Access Analyzer: $aa"
  echo "  Macie          : $mac"
  echo "  Detective      : $det"
  echo "  EBS encryption : $ebs"

  append_txt ""
  append_txt "Region: $r"
  append_txt "  GuardDuty       : $gd"
  append_txt "  Security Hub    : $sh"
  append_txt "  Inspector       : $ins"
  append_txt "  Security Lake   : $sl"
  append_txt "  AWS Config      : $cfg"
  append_txt "  Access Analyzer : $aa"
  append_txt "  Macie           : $mac"
  append_txt "  Detective       : $det"
  append_txt "  EBS encryption  : $ebs"

  # Escape pipes before markdown table output
  m() { sed 's/|/\\|/g' <<<"$1"; }
  append_md "| $r | $(m "$gd") | $(m "$sh") | $(m "$ins") | $(m "$sl") | $(m "$cfg") | $(m "$aa") | $(m "$mac") | $(m "$det") | $(m "$ebs") |"
done

# ============================================================
# CLOUDTRAIL
# ============================================================

heading "CloudTrail"

# Use list-trails once, then inspect each trail in its home region.
f="$TMP_DIR/trails.json"
if capture "$f" cloudtrail list-trails --region us-east-1; then
  TRAIL_COUNT="$(json_len "$f" "d.get('Trails',[])")"
  status_line "$INFO" "CloudTrail trails" "${TRAIL_COUNT} found"
  python3 - "$f" <<'PY' >"$TMP_DIR/trails.tsv"
import json,sys
for t in json.load(open(sys.argv[1])).get("Trails",[]):
    print("\t".join([
        t.get("Name",""),
        t.get("TrailARN",""),
        t.get("HomeRegion","")
    ]))
PY
  while IFS=$'\t' read -r name arn home; do
    [[ -z "$arn" ]] && continue
    tf="$TMP_DIR/trail-$(echo "$name"|tr -cd '[:alnum:]_-').json"
    if capture "$tf" cloudtrail get-trail-status --name "$arn" --region "${home:-us-east-1}"; then
      logging="$(json_get "$tf" "d.get('IsLogging',False)")"
      if [[ "$logging" == "True" ]]; then
        status_line "$GREEN" "CloudTrail: $name" "Logging (home region ${home})"
      else
        status_line "$RED" "CloudTrail: $name" "Exists but logging is STOPPED (home region ${home})"
      fi
    else
      status_line "$WARN" "CloudTrail: $name" "Unable to read status"
    fi
  done <"$TMP_DIR/trails.tsv"
else
  status_line "$WARN" "CloudTrail" "Unable to list trails ($(classify_error "${f}.err"))"
fi

# ============================================================
# NETWORK VISIBILITY
# ============================================================

heading "Network visibility"

for r in $REGIONS; do
  f="$TMP_DIR/flow-$r.json"
  if capture "$f" ec2 describe-flow-logs --region "$r"; then
    total="$(json_len "$f" "d.get('FlowLogs',[])")"
    active="$(python3 - "$f" <<'PY' 2>/dev/null
import json,sys
d=json.load(open(sys.argv[1]))
print(sum(1 for x in d.get("FlowLogs",[]) if x.get("FlowLogStatus")=="ACTIVE"))
PY
)"
    if [[ "$total" -gt 0 ]]; then
      status_line "$GREEN" "VPC Flow Logs ($r)" "${active}/${total} active"
    else
      status_line "$RED" "VPC Flow Logs ($r)" "None configured"
    fi
  fi
done

# ============================================================
# OPENSEARCH
# ============================================================

heading "OpenSearch security/analytics domains"

for r in $REGIONS; do
  f="$TMP_DIR/os-list-$r.json"
  if capture "$f" opensearch list-domain-names --region "$r"; then
    python3 - "$f" <<'PY' >"$TMP_DIR/os-names-$r.txt"
import json,sys
for x in json.load(open(sys.argv[1])).get("DomainNames",[]):
    print(x.get("DomainName",""))
PY
    while read -r domain; do
      [[ -z "$domain" ]] && continue
      df="$TMP_DIR/os-$r-$(echo "$domain"|tr -cd '[:alnum:]_-').json"
      if capture "$df" opensearch describe-domain --domain-name "$domain" --region "$r"; then
        python3 - "$df" "$REPORT_MD" "$REPORT_TXT" "$r" <<'PY'
import json,sys
p,md,txt,region=sys.argv[1:5]
s=json.load(open(p)).get("DomainStatus",{})
name=s.get("DomainName","?")
engine=s.get("EngineVersion","?")
cc=s.get("ClusterConfig") or {}
ebs=s.get("EBSOptions") or {}
fg=(s.get("AdvancedSecurityOptions") or {}).get("Enabled",False)
cog=(s.get("CognitoOptions") or {}).get("Enabled",False)
enc=(s.get("EncryptionAtRestOptions") or {}).get("Enabled",False)
n2n=(s.get("NodeToNodeEncryptionOptions") or {}).get("Enabled",False)
https=(s.get("DomainEndpointOptions") or {}).get("EnforceHTTPS",False)
nodes=cc.get("InstanceCount","?")
itype=cc.get("InstanceType","?")
az=cc.get("ZoneAwarenessEnabled",False)
master=cc.get("DedicatedMasterEnabled",False)
vol=ebs.get("VolumeSize","?")
line=(f"{region}: {name} | {engine} | {nodes} x {itype} | EBS {vol} GiB | "
      f"FGAC={fg} Cognito={cog} HTTPS={https} AtRest={enc} N2N={n2n} "
      f"MultiAZ={az} DedicatedMaster={master}")
print("✅  OpenSearch                    "+line)
with open(txt,"a") as f: f.write("✅  OpenSearch                    "+line+"\n")
with open(md,"a") as f: f.write(f"- ✅ **OpenSearch:** {line}\n")
PY
      fi
    done <"$TMP_DIR/os-names-$r.txt"
  fi
done

# ============================================================
# BACKUP VISIBILITY
# ============================================================

heading "AWS Backup"

for r in $REGIONS; do
  f="$TMP_DIR/backup-$r.json"
  if capture "$f" backup list-backup-vaults --region "$r"; then
    cnt="$(json_len "$f" "d.get('BackupVaultList',[])")"
    if [[ "$cnt" -gt 0 ]]; then
      status_line "$GREEN" "Backup vaults ($r)" "$cnt"
    fi
  fi
done

# ============================================================
# SUMMARY / NEXT STEP
# ============================================================

heading "How to interpret this audit"

status_line "$INFO" "Enabled" "The service/configuration was detected and can be assessed further."
status_line "$INFO" "Disabled" "No enabled configuration was detected in that Region."
status_line "$INFO" "No permission" "Your CloudShell identity cannot read that service; this is NOT proof the service is disabled."
status_line "$INFO" "Unavailable" "The API may be unsupported in that Region, blocked, or returned another error."

append_md ""
append_md "> This is an enabled-state inventory, not a penetration test or compliance certification. It intentionally does not enable services or change configuration."
append_md ""

echo
echo "============================================================"
echo "AUDIT COMPLETE"
echo "============================================================"
echo "Markdown report:"
echo "  $REPORT_MD"
echo
echo "Text report:"
echo "  $REPORT_TXT"
echo
echo "Nothing was changed in AWS."
echo
echo "Paste the SUMMARY / regional results back into ChatGPT and I can tell you:"
echo "  1. what is already good,"
echo "  2. what is missing,"
echo "  3. what is worth enabling,"
echo "  4. what may add AWS cost,"
echo "  5. what should feed into OpenSearch."
