<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Rohan Khanal</title>
    <description>The latest articles on Forem by Rohan Khanal (@rohan_khanal_e655b53e5810).</description>
    <link>https://forem.com/rohan_khanal_e655b53e5810</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3689292%2F0719369f-78d0-44d3-8d7f-3b887d96d9d3.jpg</url>
      <title>Forem: Rohan Khanal</title>
      <link>https://forem.com/rohan_khanal_e655b53e5810</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/rohan_khanal_e655b53e5810"/>
    <language>en</language>
    <item>
      <title>Automating AWS Security Scanning: Configuring Prowler on EC2 as a Cron Job to Detect Outdated AMIs</title>
      <dc:creator>Rohan Khanal</dc:creator>
      <pubDate>Sun, 11 Jan 2026 16:16:53 +0000</pubDate>
      <link>https://forem.com/rohan_khanal_e655b53e5810/automating-aws-security-scanning-configuring-prowler-on-ec2-as-a-cron-job-to-detect-outdated-amis-448f</link>
      <guid>https://forem.com/rohan_khanal_e655b53e5810/automating-aws-security-scanning-configuring-prowler-on-ec2-as-a-cron-job-to-detect-outdated-amis-448f</guid>
      <description>&lt;p&gt;&lt;strong&gt;Table of Contents&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;Prerequisites&lt;/li&gt;
&lt;li&gt;Architecture Overview&lt;/li&gt;
&lt;li&gt;Step-by-Step Implementation&lt;/li&gt;
&lt;li&gt;Bash Script Breakdown&lt;/li&gt;
&lt;li&gt;Testing and Validation&lt;/li&gt;
&lt;li&gt;Monitoring and Troubleshooting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Maintaining the security posture of your AWS infrastructure is not a one-time task; it requires continuous monitoring and assessment. One of the critical security concerns organizations face is the detection and management of outdated Amazon Machine Images (AMIs) running on EC2 instances. Outdated AMIs may contain unpatched vulnerabilities, deprecated software, and security misconfigurations that expose your infrastructure to risk.&lt;/p&gt;

&lt;p&gt;Prowler is an open-source security tool that automates AWS security assessment against industry standards including CIS Benchmarks, AWS Foundational Security Best Practices, and other compliance frameworks. By combining Prowler with EC2 cron jobs and Amazon S3 storage, you can implement a fully automated, cost-effective security scanning solution that runs on a schedule without requiring external infrastructure or significant operational overhead.&lt;/p&gt;

&lt;p&gt;This comprehensive guide demonstrates how to configure Prowler on an EC2 instance to automatically scan for outdated AMIs, generate detailed security reports in multiple formats, and securely store those reports in Amazon S3 for compliance auditing and analysis.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before implementing this solution, ensure you have the following:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AWS Account Requirements&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An active AWS account with appropriate permissions&lt;/li&gt;
&lt;li&gt;EC2 instance with Ubuntu 22.04 LTS or later (t3.small or larger recommended for adequate performance)&lt;/li&gt;
&lt;li&gt;Amazon S3 bucket for storing Prowler reports&lt;/li&gt;
&lt;li&gt;IAM role attached to the EC2 instance with the following permissions:&lt;/li&gt;
&lt;li&gt;ec2:DescribeInstances&lt;/li&gt;
&lt;li&gt;ec2:DescribeImages&lt;/li&gt;
&lt;li&gt;ec2:DescribeImageAttribute&lt;/li&gt;
&lt;li&gt;s3:PutObject&lt;/li&gt;
&lt;li&gt;s3:ListBucket&lt;/li&gt;
&lt;li&gt;logs:CreateLogGroup (optional, for CloudWatch logs)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Local System Requirements&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SSH access to the EC2 instance&lt;/li&gt;
&lt;li&gt;Basic understanding of Linux bash scripting&lt;/li&gt;
&lt;li&gt;Familiarity with AWS CLI operations&lt;/li&gt;
&lt;li&gt;Text editor (vi, nano, or similar)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;AWS CLI Configuration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ensure AWS CLI v2 is installed and configured with appropriate credentials:&lt;/p&gt;

&lt;p&gt;bash&lt;br&gt;
&lt;code&gt;aws --version&lt;br&gt;
aws sts get-caller-identity  # Verify credentials&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Architecture Overview&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution Components&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The complete automation solution consists of the following components:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;- EC2 Instance: Hosts the Prowler binary and bash automation script&lt;/li&gt;
&lt;li&gt;- Prowler: Open-source security scanning tool&lt;/li&gt;
&lt;li&gt;- Cron Scheduler: Native Linux scheduling mechanism&lt;/li&gt;
&lt;li&gt;- Bash Script: Orchestrates scan execution, report generation, and S3 upload&lt;/li&gt;
&lt;li&gt;- Amazon S3: Centralized repository for security reports&lt;/li&gt;
&lt;li&gt;- CloudWatch Logs: Optional centralized logging for audit trails&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Data Flow&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Cron Timer&lt;br&gt;
    ↓&lt;br&gt;
Bash Script (prowler-scan.sh)&lt;br&gt;
    ↓&lt;br&gt;
Prerequisite Validation&lt;br&gt;
    ↓&lt;br&gt;
Prowler Scan (ec2_instance_with_outdated_ami check)&lt;br&gt;
    ↓&lt;br&gt;
Report Generation (JSON, CSV, HTML)&lt;br&gt;
    ↓&lt;br&gt;
S3 Upload with Timestamp Organization&lt;br&gt;
    ↓&lt;br&gt;
Logging to /var/log/prowler-scan.log&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step-by-Step Implementation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 1: EC2 Instance Preparation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1.1: Create IAM Role and Policy&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Create an IAM policy document named prowler-scan-policy.json:&lt;/p&gt;

&lt;p&gt;json&lt;br&gt;
&lt;code&gt;{&lt;br&gt;
  "Version": "2012-10-17",&lt;br&gt;
  "Statement": [&lt;br&gt;
    {&lt;br&gt;
      "Sid": "ProwerEC2Permissions",&lt;br&gt;
      "Effect": "Allow",&lt;br&gt;
      "Action": [&lt;br&gt;
        "ec2:DescribeInstances",&lt;br&gt;
        "ec2:DescribeImages",&lt;br&gt;
        "ec2:DescribeImageAttribute",&lt;br&gt;
        "ec2:DescribeInstanceAttribute",&lt;br&gt;
        "ec2:DescribeInstanceStatus"&lt;br&gt;
      ],&lt;br&gt;
      "Resource": "*"&lt;br&gt;
    },&lt;br&gt;
    {&lt;br&gt;
      "Sid": "ProwlerS3Permissions",&lt;br&gt;
      "Effect": "Allow",&lt;br&gt;
      "Action": [&lt;br&gt;
        "s3:PutObject",&lt;br&gt;
        "s3:ListBucket",&lt;br&gt;
        "s3:GetBucketVersioning"&lt;br&gt;
      ],&lt;br&gt;
      "Resource": [&lt;br&gt;
        "&amp;lt;paste S3 arn here&amp;gt;",&lt;br&gt;
      ]&lt;br&gt;
    }&lt;br&gt;
  ]&lt;br&gt;
}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Create the IAM role via AWS CLI:&lt;/p&gt;

&lt;p&gt;bash&lt;br&gt;
&lt;code&gt;# Create the role&lt;br&gt;
aws iam create-role \&lt;br&gt;
  --role-name ProwlerScanRole \&lt;br&gt;
  --assume-role-policy-document '{&lt;br&gt;
    "Version": "2012-10-17",&lt;br&gt;
    "Statement": [&lt;br&gt;
      {&lt;br&gt;
        "Effect": "Allow",&lt;br&gt;
        "Principal": {&lt;br&gt;
          "Service": "ec2.amazonaws.com"&lt;br&gt;
        },&lt;br&gt;
        "Action": "sts:AssumeRole"&lt;br&gt;
      }&lt;br&gt;
    ]&lt;br&gt;
  }'&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# Create inline policy&lt;br&gt;
aws iam put-role-policy \&lt;br&gt;
  --role-name ProwlerScanRole \&lt;br&gt;
  --policy-name ProwlerScanPolicy \&lt;br&gt;
  --policy-document file://prowler-scan-policy.json&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# Create instance profile&lt;br&gt;
aws iam create-instance-profile \&lt;br&gt;
  --instance-profile-name ProwlerScanProfile&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# Add role to instance profile&lt;br&gt;
aws iam add-role-to-instance-profile \&lt;br&gt;
  --instance-profile-name ProwlerScanProfile \&lt;br&gt;
  --role-name ProwlerScanRole&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# Attach to running instance (if applicable)&lt;br&gt;
aws ec2 associate-iam-instance-profile \&lt;br&gt;
  --iam-instance-profile Name=ProwlerScanProfile \&lt;br&gt;
  --instance-id i-xxxxxxxxx&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1.2: Install Required Packages&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;SSH into your EC2 instance and update system packages:&lt;/p&gt;

&lt;p&gt;bash&lt;br&gt;
&lt;code&gt;# Update package manager&lt;br&gt;
sudo apt-get update&lt;br&gt;
sudo apt-get upgrade -y&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# Install pipx (required for Prowler)&lt;br&gt;
sudo apt install pipx&lt;br&gt;
pipx ensurepath&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# Prowler installation&lt;br&gt;
pipx install prowler&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# Install unzip and curl&lt;br&gt;
sudo apt install unzip curl&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# Install AWS CLI v2&lt;br&gt;
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"&lt;br&gt;
unzip awscliv2.zip&lt;br&gt;
sudo ./aws/install&lt;br&gt;
rm -rf aws awscliv2.zip&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# Verify installations&lt;/code&gt;&lt;br&gt;
&lt;code&gt;prowler -v&lt;/code&gt;&lt;br&gt;
&lt;code&gt;aws --version&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1.3: Create Amazon S3 Bucket&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;bash&lt;br&gt;
&lt;code&gt;# Create S3 bucket with unique name&lt;br&gt;
aws s3 mb s3://&amp;lt;your bucket name&amp;gt; \&lt;br&gt;
  --region ap-south-1&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# Enable versioning for audit trail&lt;br&gt;
aws s3api put-bucket-versioning \&lt;br&gt;
  --bucket &amp;lt;your bucket name&amp;gt; \&lt;br&gt;
  --versioning-configuration Status=Enabled&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# Enable encryption (AES256 default)&lt;br&gt;
aws s3api put-bucket-encryption \&lt;br&gt;
  --bucket &amp;lt;your bucket name&amp;gt; \&lt;br&gt;
  --server-side-encryption-configuration '{&lt;br&gt;
    "Rules": [{&lt;br&gt;
      "ApplyServerSideEncryptionByDefault": {&lt;br&gt;
        "SSEAlgorithm": "AES256"&lt;br&gt;
      }&lt;br&gt;
    }]&lt;br&gt;
  }'&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# Block public access&lt;br&gt;
aws s3api put-public-access-block \&lt;br&gt;
  --bucket &amp;lt;your bucket name&amp;gt; \&lt;br&gt;
  --public-access-block-configuration \&lt;br&gt;
    "BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 2: Prowler Test Scan&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2.1: Validate Prowler Permissions&lt;/strong&gt;&lt;br&gt;
Test Prowler with your EC2 instance credentials:&lt;/p&gt;

&lt;p&gt;bash&lt;br&gt;
&lt;code&gt;# Run a quick test scan on one check&lt;br&gt;
prowler aws --checks ec2_instance_with_outdated_ami -f ap-south-1&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# Verify output directory&lt;br&gt;
ls -la output/&lt;/code&gt;&lt;br&gt;
Note:&lt;br&gt;
If you encounter a “prowler: command not found” error, first locate where Prowler is installed by running:&lt;br&gt;
which prowler&lt;/p&gt;

&lt;p&gt;Example output:&lt;br&gt;
&lt;code&gt;/home/ubuntu/.local/bin/prowler&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If Prowler is installed in a custom path, use the full path instead of just prowler in your command. For example:&lt;br&gt;
&lt;code&gt;/home/ubuntu/.local/bin/prowler aws --checks ec2_instance_with_outdated_ami -f ap-south-1&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 3: Directory and File Setup&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3.1: Create Directory Structure&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;bash&lt;br&gt;
 Create output directory for Prowler reports&lt;/p&gt;

&lt;p&gt;Note:&lt;br&gt;
Mostly prowler creates this on its own you just need to change the permission and ownership.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sudo mkdir -p /home/ubuntu/output&lt;br&gt;
sudo chown ubuntu:ubuntu /home/ubuntu/output&lt;br&gt;
sudo chmod 755 /home/ubuntu/output&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# Create log directory&lt;br&gt;
sudo mkdir -p /var/log&lt;br&gt;
sudo touch /var/log/prowler-scan.log&lt;br&gt;
sudo chown ubuntu:ubuntu /var/log/prowler-scan.log&lt;br&gt;
sudo chmod 644 /var/log/prowler-scan.log&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# Create lock file directory&lt;br&gt;
sudo mkdir -p /var/run&lt;br&gt;
sudo touch /var/run/prowler-scan.lock&lt;br&gt;
sudo chown ubuntu:ubuntu /var/run/prowler-scan.lock&lt;br&gt;
sudo chmod 744 /var/run/prowler-scan.lock&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# Verify permissions&lt;br&gt;
ls -la /home/ubuntu/output&lt;br&gt;
ls -la /var/log/prowler-scan.log&lt;br&gt;
ls -la /var/run/prowler-scan.lock&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# Expected output (Respectively)&lt;br&gt;
drwxr-xr-x 2 ubuntu ubuntu 4096 Jan 11 12:34 /home/ubuntu/output&lt;br&gt;
-rw-r--r-- 1 ubuntu ubuntu 0 Jan 11 12:34 /var/log/prowler-scan.log&lt;br&gt;
-rwxr--r-- 1 ubuntu ubuntu 0 Jan 11 12:34 /var/run/prowler-scan.lock&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 4: Bash Script Creation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4.1: Create the Main Automation Script&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Create the file /usr/local/bin/prowler-scan.sh:&lt;/p&gt;

&lt;p&gt;bash&lt;br&gt;
&lt;code&gt;sudo vi /usr/local/bin/prowler-scan.sh&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Paste the following script:&lt;/p&gt;

&lt;p&gt;bash&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/bin/bash

##################################################################
# Prowler Security Scan Automation Script for EC2
##################################################################

set -uo pipefail   # Exit on undefined variables and pipe failures
IFS=$'\n\t'        # Set Internal Field Separator for safer word splitting

umask 002

# Set PATH for cron environment
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/home/ubuntu/.local/bin
export PATH

################################################################################
# Configuration Variables
################################################################################
PROWLER_BIN="/home/ubuntu/prowler/prowler"
OUTPUT_DIR="/home/ubuntu/output"
S3_BUCKET="ami-prowler-report-bucket"
S3_PREFIX="prowler-reports"
AWS_REGION="ap-south-1"
CHECKS="ec2_instance_with_outdated_ami"
LOG_FILE="/var/log/prowler-scan.log"
LOCK_FILE="/var/run/prowler-scan.lock"

################################################################################
# Color codes for output
################################################################################
readonly RED='\033[0;31m'
readonly GREEN='\033[0;32m'
readonly YELLOW='\033[1;33m'
readonly NC='\033[0m' # No Color

################################################################################
# Logging Functions
################################################################################
log_info() {
    local msg="[INFO] $(date '+%Y-%m-%d %H:%M:%S') - $*"
    echo -e "${GREEN}${msg}${NC}"
    echo "$msg" &amp;gt;&amp;gt; "$LOG_FILE" 2&amp;gt;/dev/null || true
}

log_warn() {
    local msg="[WARN] $(date '+%Y-%m-%d %H:%M:%S') - $*"
    echo -e "${YELLOW}${msg}${NC}" &amp;gt;&amp;amp;2
    echo "$msg" &amp;gt;&amp;gt; "$LOG_FILE" 2&amp;gt;/dev/null || true
}

log_error() {
    local msg="[ERROR] $(date '+%Y-%m-%d %H:%M:%S') - $*"
    echo -e "${RED}${msg}${NC}" &amp;gt;&amp;amp;2
    echo "$msg" &amp;gt;&amp;gt; "$LOG_FILE" 2&amp;gt;/dev/null || true
}

################################################################################
# Error Handler
################################################################################
error_exit() {
    log_error "$1"
    exit "${2:-1}"
}

################################################################################
# Cleanup Function
################################################################################
cleanup() {
    local exit_code=$?
    if [ $exit_code -ne 0 ]; then
        log_warn "Script exited with error code: $exit_code"
    fi
    # Remove lock file if it exists
    if [ -f "$LOCK_FILE" ]; then
        rm -f "$LOCK_FILE"
        log_info "Lock file removed"
    fi
}

trap cleanup EXIT

################################################################################
# Lock Management (Prevent concurrent runs)
################################################################################
acquire_lock() {
    if [ -f "$LOCK_FILE" ]; then
        local lock_pid=$(cat "$LOCK_FILE" 2&amp;gt;/dev/null || echo "")
        if [ -n "$lock_pid" ] &amp;amp;&amp;amp; kill -0 "$lock_pid" 2&amp;gt;/dev/null; then
            log_warn "Another instance is already running (PID: $lock_pid)"
            exit 0
        else
            log_warn "Stale lock file found, removing it"
            rm -f "$LOCK_FILE"
        fi
    fi
    echo $$ &amp;gt; "$LOCK_FILE"
    log_info "Lock acquired (PID: $$)"
}

################################################################################
# Validation Functions
################################################################################
validate_prerequisites() {
    log_info "Validating prerequisites..."

    # Check if prowler binary exists
    if [ ! -f "$PROWLER_BIN" ]; then
        error_exit "Prowler binary not found at: $PROWLER_BIN" 1
    fi

    # Check if prowler is executable
    if [ ! -x "$PROWLER_BIN" ]; then
        error_exit "Prowler binary is not executable: $PROWLER_BIN" 1
    fi

    # Check if output directory exists, create if not
    if [ ! -d "$OUTPUT_DIR" ]; then
        log_warn "Output directory not found. Creating: $OUTPUT_DIR"
        mkdir -p "$OUTPUT_DIR" || error_exit "Failed to create output directory: $OUTPUT_DIR" 1
    fi

    # Check if AWS CLI is installed
    if ! command -v aws &amp;amp;&amp;gt; /dev/null; then
        error_exit "AWS CLI is not installed or not in PATH" 1
    fi

    # Check AWS credentials
    if ! aws sts get-caller-identity &amp;amp;&amp;gt; /dev/null; then
        error_exit "AWS credentials not configured or invalid" 1
    fi

    log_info "Prerequisites validation completed successfully"
}

################################################################################
# Run Prowler Scan
################################################################################
run_prowler_scan() {
    log_info "Starting Prowler security scan..."
    log_info "Check: $CHECKS"
    log_info "Region: $AWS_REGION"

    # Change to output directory to ensure report files are created there
    cd "$OUTPUT_DIR" || error_exit "Failed to change directory to $OUTPUT_DIR" 1

    # Run prowler and capture exit code
    # Prowler exit codes:
    #   0 = All checks passed (no findings)
    #   3 = Some checks failed (security findings detected) - THIS IS NORMAL
    #   Other = Actual execution error

    "$PROWLER_BIN" aws --checks "$CHECKS" -f "$AWS_REGION" 2&amp;gt;&amp;amp;1 | tee -a "$LOG_FILE"
    local prowler_exit_code=${PIPESTATUS}

    if [ $prowler_exit_code -eq 0 ]; then
        log_info "Prowler scan completed - No security issues found"
        return 0
    elif [ $prowler_exit_code -eq 3 ]; then
        log_warn "Prowler scan completed with findings (exit code 3)"
        log_info "Security issues were detected - this is NORMAL behavior"
        log_info "Reports will be processed and uploaded to S3"
        return 0
    else
        error_exit "Prowler scan encountered an execution error (exit code: $prowler_exit_code)" $prowler_exit_code
    fi
}

################################################################################
# Find Latest Prowler Output Files
################################################################################
find_latest_reports() {
    log_info "Searching for generated report files in: $OUTPUT_DIR"

    # Find all three report types (most recent files modified in last 10 minutes)
    OCSF_FILE=$(find "$OUTPUT_DIR" -name "prowler-output-*.ocsf.json" -type f -mmin -10 2&amp;gt;/dev/null | sort -r | head -n1)
    CSV_FILE=$(find "$OUTPUT_DIR" -name "prowler-output-*.csv" -type f -mmin -10 2&amp;gt;/dev/null | sort -r | head -n1)
    HTML_FILE=$(find "$OUTPUT_DIR" -name "prowler-output-*.html" -type f -mmin -10 2&amp;gt;/dev/null | sort -r | head -n1)

    # Validate that all files were found
    if [ -z "$OCSF_FILE" ] || [ -z "$CSV_FILE" ] || [ -z "$HTML_FILE" ]; then
        log_error "Missing report files:"
        [ -z "$OCSF_FILE" ] &amp;amp;&amp;amp; log_error "  - OCSF JSON file not found"
        [ -z "$CSV_FILE" ] &amp;amp;&amp;amp; log_error "  - CSV file not found"
        [ -z "$HTML_FILE" ] &amp;amp;&amp;amp; log_error "  - HTML file not found"
        error_exit "Could not find all required report files in $OUTPUT_DIR" 1
    fi

    # Validate files exist and are readable
    for file in "$OCSF_FILE" "$CSV_FILE" "$HTML_FILE"; do
        if [ ! -r "$file" ]; then
            error_exit "Report file not readable: $file" 1
        fi
    done

    log_info "Found OCSF JSON report: $(basename "$OCSF_FILE")"
    log_info "Found CSV report: $(basename "$CSV_FILE")"
    log_info "Found HTML report: $(basename "$HTML_FILE")"
}

################################################################################
# Upload Files to S3
################################################################################
upload_to_s3() {
    local file="$1"
    local s3_folder="$2"
    local filename=$(basename "$file")

    local s3_path="s3://${S3_BUCKET}/${S3_PREFIX}/${s3_folder}/${filename}"

    log_info "Uploading: $filename -&amp;gt; $s3_path"

    if aws s3 cp "$file" "$s3_path" \
        --region "$AWS_REGION" \
        --sse AES256 \
        --no-progress 2&amp;gt;&amp;amp;1 | tee -a "$LOG_FILE"; then
        log_info "Successfully uploaded: $filename"
        return 0
    else
        log_error "Failed to upload: $filename"
        return 1
    fi
}

################################################################################
# Upload All Reports to S3
################################################################################
upload_all_reports() {
    log_info "Starting S3 upload process..."

    # Create S3 folder name with human-readable timestamp format
    # Format: YYYY-MM-DD_HH-MM-SS
    local S3_FOLDER_NAME=$(date '+%Y-%m-%d_%H-%M-%S')
    log_info "Using S3 folder name: $S3_FOLDER_NAME"

    local upload_failed=0

    # Upload OCSF JSON
    if ! upload_to_s3 "$OCSF_FILE" "$S3_FOLDER_NAME"; then
        upload_failed=1
    fi

    # Upload CSV
    if ! upload_to_s3 "$CSV_FILE" "$S3_FOLDER_NAME"; then
        upload_failed=1
    fi

    # Upload HTML
    if ! upload_to_s3 "$HTML_FILE" "$S3_FOLDER_NAME"; then
        upload_failed=1
    fi

    if [ $upload_failed -eq 1 ]; then
        error_exit "One or more file uploads failed" 1
    fi

    log_info "All reports uploaded successfully to S3"
    log_info "S3 Location: s3://${S3_BUCKET}/${S3_PREFIX}/${S3_FOLDER_NAME}/"
}

################################################################################
# Main Function
################################################################################
main() {
    # Initialize log file
    touch "$LOG_FILE" 2&amp;gt;/dev/null || LOG_FILE="/tmp/prowler-scan.log"

    log_info "=========================================="
    log_info "Prowler Security Scan Script Started"
    log_info "Version: 2.0 (Fixed Exit Code Handling)"
    log_info "=========================================="
    log_info "Scanning Account: $(aws sts get-caller-identity --query Account --output text)"
    log_info "AWS Region: $AWS_REGION"

    # Acquire lock to prevent concurrent runs
    acquire_lock

    # Step 1: Validate prerequisites
    validate_prerequisites

    # Step 2: Run Prowler scan
    run_prowler_scan

    # Step 3: Find generated report files
    find_latest_reports

    # Step 4: Upload reports to S3
    upload_all_reports

    log_info "=========================================="
    log_info "Script Completed Successfully"
    log_info "=========================================="
}

################################################################################
# Script Entry Point
################################################################################
main "$@" 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 4.2: Set Script Permissions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;bash&lt;br&gt;
&lt;code&gt;# Make script executable&lt;br&gt;
sudo chmod +x /usr/local/bin/prowler-scan.sh&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# Change ownership to ubuntu user&lt;br&gt;
sudo chown ubuntu:ubuntu /usr/local/bin/prowler-scan.sh&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# Verify permissions&lt;br&gt;
ls -la /usr/local/bin/prowler-scan.sh&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4.3: Test Script Execution&lt;/strong&gt;&lt;br&gt;
bash&lt;br&gt;
&lt;code&gt;# Run the script manually to ensure it works&lt;br&gt;
/usr/local/bin/prowler-scan.sh&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# Check log output&lt;br&gt;
tail -f /var/log/prowler-scan.log&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# Verify S3 upload&lt;br&gt;
aws s3 ls s3://&amp;lt;your_s3_bucket_name&amp;gt;/prowler-reports/&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Phase 5: Cron Job Configuration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 5.1: Edit Crontab&lt;/strong&gt;&lt;br&gt;
bash&lt;br&gt;
&lt;code&gt;# Open crontab editor for current user&lt;br&gt;
crontab -e&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# If first time, select editor (usually nano or vi)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 5.2: Add Cron Expression&lt;/strong&gt;&lt;br&gt;
Add the following line to run the scan daily at 2 AM:&lt;br&gt;
Note:&lt;br&gt;
Use cronguru to generate custom schedules.&lt;/p&gt;

&lt;p&gt;bash&lt;br&gt;
&lt;code&gt;# Daily Prowler scan - runs every day at 2:00 AM&lt;br&gt;
0 2 * * * /usr/local/bin/prowler-scan.sh&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# Alternative: Weekly scan on Sunday at 2:00 AM&lt;br&gt;
0 2 ? * SUN /usr/local/bin/prowler-scan.sh&lt;br&gt;
&lt;/code&gt;&lt;br&gt;
&lt;code&gt;# Alternative: Twice daily (2 AM and 2 PM)&lt;br&gt;
0 2,14 * * * /usr/local/bin/prowler-scan.sh&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 5.3: Verify Cron Configuration&lt;/strong&gt;&lt;br&gt;
bash&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# List scheduled cron jobs&lt;br&gt;
crontab -l&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# Monitor cron execution&lt;br&gt;
sudo tail -f /var/log/syslog | grep CRON&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# Alternative: Check cron logs (varies by system)&lt;br&gt;
sudo journalctl -u cron --follow&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bash Script Breakdown&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Script Architecture&lt;/strong&gt;&lt;br&gt;
The automation script follows AWS best practices for &lt;br&gt;
production-grade bash scripting:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Header and Metadata&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Script version and purpose declaration&lt;/li&gt;
&lt;li&gt;License information for compliance&lt;/li&gt;
&lt;li&gt;Clear authorship for maintainability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Strict Mode Configuration&lt;/strong&gt;&lt;br&gt;
bash&lt;/p&gt;

&lt;p&gt;&lt;code&gt;set -uo pipefail&lt;br&gt;
IFS=$'\n\t'&lt;br&gt;
umask 002&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;set -u: Fails on undefined variable references (prevents silent errors)&lt;/li&gt;
&lt;li&gt;set -o pipefail: Pipe command fails if any command fails&lt;/li&gt;
&lt;li&gt;IFS: Prevents word splitting issues in shell loops&lt;/li&gt;
&lt;li&gt;umask 002: Sets secure default file permissions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Path and Environment Setup&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;bash&lt;br&gt;
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/home/ubuntu/.local/bin&lt;br&gt;
export PATH&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Essential for cron execution, which doesn't inherit the user's PATH.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Logging System&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Color-coded output: Different colors for INFO, WARN, ERROR messages&lt;/li&gt;
&lt;li&gt;Dual logging: Console output + file logging for audit trails&lt;/li&gt;
&lt;li&gt;Timestamp inclusion: Every log entry includes date/time for forensics&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;5. Error Handling and Recovery&lt;/strong&gt;&lt;br&gt;
bash&lt;br&gt;
&lt;code&gt;trap cleanup EXIT&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Ensures lock file removal and cleanup occurs regardless of exit condition.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Lock Mechanism&lt;/strong&gt;&lt;br&gt;
Prevents concurrent Prowler executions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Checks for existing lock file with PID validation&lt;/li&gt;
&lt;li&gt;Detects stale locks (process no longer running)&lt;/li&gt;
&lt;li&gt;Acquires new lock when safe to proceed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;7. Prerequisite Validation&lt;/strong&gt;&lt;br&gt;
Before running expensive operations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Verifies Prowler binary existence and executability&lt;/li&gt;
&lt;li&gt;Confirms AWS CLI availability&lt;/li&gt;
&lt;li&gt;Validates AWS credentials&lt;/li&gt;
&lt;li&gt;Creates missing directories with appropriate error handling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;8. Prowler Execution&lt;/strong&gt;&lt;br&gt;
bash&lt;br&gt;
&lt;code&gt;"$PROWLER_BIN" aws --checks "$CHECKS" -f "$AWS_REGION" 2&amp;gt;&amp;amp;1 | tee -a "$LOG_FILE"&lt;br&gt;
local prowler_exit_code=${PIPESTATUS}&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Redirects stderr to stdout for complete logging&lt;/li&gt;
&lt;li&gt;Tees output to both console and log file&lt;/li&gt;
&lt;li&gt;Captures exit code from PROWLER_BIN (not tee)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;9. Exit Code Handling&lt;/strong&gt;&lt;br&gt;
Prowler uses non-standard exit codes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;0: Checks passed (no findings)&lt;/li&gt;
&lt;li&gt;3: Checks failed with findings (NORMAL, not an error)&lt;/li&gt;
&lt;li&gt;Other: Actual execution errors&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;10. Report Discovery&lt;/strong&gt;&lt;br&gt;
bash&lt;br&gt;
&lt;code&gt;OCSF_FILE=$(find "$OUTPUT_DIR" -name "prowler-output-*.ocsf.json" -type f -mmin -10 2&amp;gt;/dev/null | sort -r | head -n1)&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Finds files modified in last 10 minutes (prevents stale report uploads)&lt;/li&gt;
&lt;li&gt;Sorts by modification time to get most recent&lt;/li&gt;
&lt;li&gt;Validates all three report formats exist before proceeding&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;11. S3 Upload with Security&lt;/strong&gt;&lt;br&gt;
bash&lt;br&gt;
&lt;code&gt;aws s3 cp "$file" "$s3_path" \&lt;br&gt;
    --region "$AWS_REGION" \&lt;br&gt;
    --sse AES256 \&lt;br&gt;
    --no-progress&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enforces AES256 server-side encryption&lt;/li&gt;
&lt;li&gt;Organizes reports by timestamp for easy retrieval&lt;/li&gt;
&lt;li&gt;Validates upload success before declaring completion&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Testing and Validation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Manual Execution Tests&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test 1: Dry Run with Single Report&lt;/strong&gt;&lt;br&gt;
bash&lt;br&gt;
&lt;code&gt;# Execute script with verbose output&lt;br&gt;
bash -x /usr/local/bin/prowler-scan.sh&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# Expected output:&lt;br&gt;
&lt;/code&gt;# [INFO] ... Prowler Security Scan Script Started&lt;code&gt;&lt;br&gt;
&lt;/code&gt;# [INFO] ... Validating prerequisites...&lt;code&gt;&lt;br&gt;
&lt;/code&gt;# [INFO] ... Lock acquired (PID: XXXX)&lt;code&gt;&lt;br&gt;
&lt;/code&gt;# [INFO] ... Starting Prowler security scan...&lt;code&gt;&lt;br&gt;
&lt;/code&gt;# [INFO] ... Successfully uploaded: prowler-output-*.ocsf.json&lt;code&gt;&lt;br&gt;
&lt;/code&gt;# [INFO] ... All reports uploaded successfully to S3`&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test 2: Verify Log Output&lt;/strong&gt;&lt;br&gt;
bash&lt;br&gt;
&lt;code&gt;# Check last 50 lines of log&lt;br&gt;
tail -50 /var/log/prowler-scan.log&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# Search for errors&lt;br&gt;
grep ERROR /var/log/prowler-scan.log&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# Count successful uploads&lt;br&gt;
grep "Successfully uploaded" /var/log/prowler-scan.log | wc -l&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test 3: Validate S3 Bucket Structure&lt;/strong&gt;&lt;br&gt;
bash&lt;br&gt;
&lt;code&gt;# List all uploaded reports with timestamps&lt;br&gt;
aws s3 ls s3://ami-prowler-report-bucket/prowler-reports/ --recursive&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# Verify encryption on specific object&lt;br&gt;
aws s3api head-object \&lt;br&gt;
    --bucket ami-prowler-report-bucket \&lt;br&gt;
    --key prowler-reports/2024-01-15_02-00-00/prowler-output-123456789012-20240115T020000Z.ocsf.json&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# Expected output includes: ServerSideEncryption: AES256&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test 4: Concurrent Execution Prevention&lt;/strong&gt;&lt;br&gt;
bash&lt;br&gt;
&lt;code&gt;# Terminal 1: Start first execution&lt;br&gt;
/usr/local/bin/prowler-scan.sh&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# Terminal 2: While first is running, try to start another&lt;br&gt;
/usr/local/bin/prowler-scan.sh&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# Expected: Second script exits immediately with warning about concurrent instance&lt;/code&gt;&lt;br&gt;
&lt;code&gt;# Output: [WARN] Another instance is already running (PID: XXXX)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cron Execution Validation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Simulate Cron Environment&lt;/strong&gt;&lt;br&gt;
bash&lt;br&gt;
&lt;code&gt;# Run script with cron environment variables&lt;/code&gt;&lt;br&gt;
&lt;code&gt;env -i HOME=$HOME /usr/sbin/cron -f&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Monitor Cron Logs&lt;/strong&gt;&lt;br&gt;
bash&lt;br&gt;
&lt;code&gt;# Watch cron execution in real-time (requires enabling cron logging)&lt;/code&gt;&lt;br&gt;
&lt;code&gt;sudo tail -f /var/log/cron&lt;/code&gt;&lt;br&gt;
&lt;code&gt;# or&lt;/code&gt;&lt;br&gt;
&lt;code&gt;sudo journalctl -u cron -f&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Create Test Cron Entry&lt;/strong&gt;&lt;br&gt;
bash&lt;br&gt;
&lt;code&gt;# Edit crontab&lt;br&gt;
crontab -e&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# Add test job to run in 2 minutes&lt;br&gt;
*/2 * * * * /usr/local/bin/prowler-scan.sh&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# Wait 3 minutes, then verify execution&lt;/code&gt;&lt;br&gt;
&lt;code&gt;sleep 180&lt;/code&gt;&lt;br&gt;
&lt;code&gt;tail -20 /var/log/prowler-scan.log&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Monitoring and Troubleshooting&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Common Issues and Solutions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Issue 1: Prowler Not Found&lt;/strong&gt;&lt;br&gt;
Symptom: Prowler not found.&lt;/p&gt;

&lt;p&gt;Solutions:&lt;br&gt;
bash&lt;br&gt;
&lt;code&gt;# Verify installation&lt;br&gt;
which prowler&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# Try running prowler this way. &lt;br&gt;
/home/ubuntu/.local/prowler aws --checks ec2_instance_with_outdated_ami -f ap-south-1&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# Reinstall if necessary&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Issue 2: AWS Credentials Not Found&lt;/strong&gt;&lt;br&gt;
Symptom: AWS credentials not configured or invalid&lt;/p&gt;

&lt;p&gt;Solutions:&lt;br&gt;
bash&lt;br&gt;
&lt;code&gt;# Check IAM instance profile attachment&lt;br&gt;
aws sts get-caller-identity&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# If using IAM keys, verify in ~/.aws/credentials&lt;br&gt;
cat ~/.aws/credentials&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# Troubleshoot IAM role&lt;br&gt;
aws ec2 describe-instances --instance-ids i-xxxxxxxxx \&lt;br&gt;
  --query 'Reservations.Instances.IamInstanceProfile'&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Issue 3: Report Files Not Found in Output Directory&lt;/strong&gt;&lt;br&gt;
Symptom: Could not find all required report files&lt;/p&gt;

&lt;p&gt;Solutions:&lt;br&gt;
bash&lt;br&gt;
&lt;code&gt;# Check output directory exists and has correct permissions&lt;br&gt;
ls -la /home/ubuntu/output/&lt;br&gt;
df -h /home/ubuntu  # Check disk space&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# Run Prowler manually to debug&lt;br&gt;
cd /home/ubuntu/output&lt;br&gt;
/home/ubuntu/prowler/prowler aws --checks ec2_instance_with_outdated_ami -f ap-south-1&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# Verify report generation&lt;br&gt;
ls -lh prowler-output-*&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Issue 4: S3 Upload Failures&lt;/strong&gt;&lt;br&gt;
Symptom: Failed to upload: prowler-output-*.csv&lt;/p&gt;

&lt;p&gt;Solutions:&lt;br&gt;
bash&lt;br&gt;
&lt;code&gt;# Verify S3 bucket exists and is accessible&lt;br&gt;
aws s3 ls s3://&amp;lt;your_s3_bucket_name&amp;gt;/&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# Check IAM permissions&lt;br&gt;
aws iam simulate-principal-policy \&lt;br&gt;
  --policy-source-arn &amp;lt;your_s3_bucket_arn&amp;gt; \&lt;br&gt;
  --action-names s3:PutObject s3:ListBucket \&lt;br&gt;
  --resource-arns &amp;lt;your_s3_bucket_arn&amp;gt;/*&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# Test S3 upload manually&lt;br&gt;
echo "test" &amp;gt; /tmp/test.txt&lt;br&gt;
aws s3 cp /tmp/test.txt s3://&amp;lt;your_s3_bucket_name&amp;gt;/test.txt --sse AES256&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# Check S3 bucket policy&lt;br&gt;
aws s3api get-bucket-policy --bucket &amp;lt;your_s3_bucket_name&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Issue 5: Concurrent Execution Lock Issues&lt;/strong&gt;&lt;br&gt;
Symptom: Script exits immediately, Another instance is already running&lt;/p&gt;

&lt;p&gt;Solutions:&lt;br&gt;
bash&lt;br&gt;
&lt;code&gt;# Check for stale lock&lt;br&gt;
cat /var/run/prowler-scan.lock&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# Verify if PID is still running&lt;br&gt;
ps aux | grep [PID]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# If stale, manually remove lock&lt;br&gt;
rm /var/run/prowler-scan.lock&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# Increase scan timeout or adjust schedule&lt;/code&gt;&lt;br&gt;
&lt;code&gt;# Modify LOCK_FILE check in script if scans consistently exceed frequency&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;S3 Lifecycle Policies&lt;/strong&gt;&lt;br&gt;
bash&lt;br&gt;
&lt;code&gt;# Archive old reports to Glacier after 30 days&lt;br&gt;
aws s3api put-bucket-lifecycle-configuration \&lt;br&gt;
  --bucket &amp;lt;your_s3_bucket_name&amp;gt; \&lt;br&gt;
  --lifecycle-configuration '{&lt;br&gt;
    "Rules": [{&lt;br&gt;
      "Id": "ArchiveOldReports",&lt;br&gt;
      "Status": "Enabled",&lt;br&gt;
      "Prefix": "prowler-reports/",&lt;br&gt;
      "Transitions": [{&lt;br&gt;
        "Days": 30,&lt;br&gt;
        "StorageClass": "GLACIER"&lt;br&gt;
      }]&lt;br&gt;
    }]&lt;br&gt;
  }'&lt;/code&gt;&lt;/p&gt;

</description>
      <category>automation</category>
      <category>aws</category>
      <category>devops</category>
      <category>security</category>
    </item>
  </channel>
</rss>
