Prowler Cheat Sheet
Overview
Prowler is an open-source security tool for AWS security assessments, audits, incident response, continuous monitoring, hardening and forensics readiness. It contains hundreds of controls covering CIS, PCI DSS, ISO27001, GDPR, HIPAA, FFIEC, SOC2, AWS FTR, ENS and custom security frameworks. Prowler is a command-line tool based on AWS CLI commands and one-liner scripts that use AWS APIs to check security best practices.
💡 Key Features: 300+ security checks, multiple compliance frameworks, multi-account support, custom checks, detailed reporting, CI/CD integration, and extensive output formats including JSON, CSV, HTML, and JUnit XML.
Installation and Setup
Python Package Installation
# Install Python 3.9+ (required)
python3 --version
# Install Prowler via pip
pip3 install prowler
# Alternative: Install with all dependencies
pip3 install prowler[all]
# Verify installation
prowler --version
# Update Prowler
pip3 install --upgrade prowler
# Install specific version
pip3 install prowler==3.12.0
Docker Installation
# Pull Prowler Docker image
docker pull toniblyx/prowler:latest
# Run Prowler in Docker
docker run --rm -it \
-v ~/.aws:/root/.aws \
toniblyx/prowler:latest \
aws --help
# Create Docker alias for easier usage
echo 'alias prowler="docker run --rm -it -v ~/.aws:/root/.aws -v $(pwd):/prowler/output toniblyx/prowler:latest"' >> ~/.bashrc
source ~/.bashrc
# Run with volume mounts for output
docker run --rm -it \
-v ~/.aws:/root/.aws \
-v $(pwd)/output:/prowler/output \
toniblyx/prowler:latest \
aws --output-directory /prowler/output
# Create Docker Compose file
cat > docker-compose.yml << 'EOF'
version: '3.8'
services:
prowler:
image: toniblyx/prowler:latest
volumes:
- ~/.aws:/root/.aws
- ./output:/prowler/output
environment:
- AWS_PROFILE=default
- AWS_DEFAULT_REGION=us-east-1
EOF
# Run with Docker Compose
docker-compose run prowler aws --output-directory /prowler/output
Source Installation
# Clone Prowler repository
git clone https://github.com/prowler-cloud/prowler.git
cd prowler
# Install dependencies
pip3 install -r requirements.txt
# Make executable
chmod +x prowler
# Add to PATH
echo 'export PATH=$PATH:'$(pwd) >> ~/.bashrc
source ~/.bashrc
# Verify installation
./prowler --version
# Create symbolic link
sudo ln -sf $(pwd)/prowler /usr/local/bin/prowler
AWS Configuration
# Install AWS CLI
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install
# Configure AWS credentials
aws configure
# Enter: Access Key ID, Secret Access Key, Region, Output format
# Alternative: Use environment variables
export AWS_ACCESS_KEY_ID="your-access-key"
export AWS_SECRET_ACCESS_KEY="your-secret-key"
export AWS_DEFAULT_REGION="us-east-1"
# Alternative: Use IAM roles (recommended for EC2)
# Attach IAM role with required permissions to EC2 instance
# Test AWS configuration
aws sts get-caller-identity
# Configure multiple profiles
aws configure --profile production
aws configure --profile development
aws configure --profile staging
# List configured profiles
aws configure list-profiles
# Set default profile
export AWS_PROFILE=production
# Required AWS permissions for Prowler
cat > prowler-policy.json << 'EOF'
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"access-analyzer:List*",
"account:Get*",
"acm:Describe*",
"acm:List*",
"apigateway:GET",
"application-autoscaling:Describe*",
"appstream:Describe*",
"appstream:List*",
"autoscaling:Describe*",
"backup:List*",
"cloudformation:Describe*",
"cloudformation:Get*",
"cloudformation:List*",
"cloudfront:Get*",
"cloudfront:List*",
"cloudtrail:Describe*",
"cloudtrail:Get*",
"cloudtrail:List*",
"cloudwatch:Describe*",
"cloudwatch:Get*",
"cloudwatch:List*",
"codebuild:List*",
"config:Describe*",
"config:Get*",
"config:List*",
"dax:Describe*",
"dax:List*",
"directconnect:Describe*",
"dms:Describe*",
"dms:List*",
"ds:Describe*",
"ds:Get*",
"ds:List*",
"dynamodb:Describe*",
"dynamodb:List*",
"ec2:Describe*",
"ec2:Get*",
"ecr:Describe*",
"ecr:Get*",
"ecr:List*",
"ecs:Describe*",
"ecs:List*",
"efs:Describe*",
"eks:Describe*",
"eks:List*",
"elasticache:Describe*",
"elasticbeanstalk:Describe*",
"elasticfilesystem:Describe*",
"elasticloadbalancing:Describe*",
"elasticmapreduce:Describe*",
"elasticmapreduce:List*",
"es:Describe*",
"es:List*",
"events:Describe*",
"events:List*",
"firehose:Describe*",
"firehose:List*",
"fsx:Describe*",
"fsx:List*",
"glue:Get*",
"glue:List*",
"guardduty:Get*",
"guardduty:List*",
"iam:Generate*",
"iam:Get*",
"iam:List*",
"iam:Simulate*",
"inspector:Describe*",
"inspector:Get*",
"inspector:List*",
"kinesis:Describe*",
"kinesis:List*",
"kms:Describe*",
"kms:Get*",
"kms:List*",
"lambda:Get*",
"lambda:List*",
"logs:Describe*",
"logs:Get*",
"logs:List*",
"macie2:Get*",
"macie2:List*",
"organizations:Describe*",
"organizations:List*",
"rds:Describe*",
"rds:List*",
"redshift:Describe*",
"route53:Get*",
"route53:List*",
"route53domains:Get*",
"route53domains:List*",
"s3:Get*",
"s3:List*",
"sagemaker:Describe*",
"sagemaker:List*",
"secretsmanager:Describe*",
"secretsmanager:Get*",
"secretsmanager:List*",
"securityhub:Describe*",
"securityhub:Get*",
"securityhub:List*",
"ses:Get*",
"ses:List*",
"shield:Describe*",
"shield:Get*",
"shield:List*",
"sns:Get*",
"sns:List*",
"sqs:Get*",
"sqs:List*",
"ssm:Describe*",
"ssm:Get*",
"ssm:List*",
"support:Describe*",
"trustedadvisor:Describe*",
"waf:Get*",
"waf:List*",
"wafv2:Get*",
"wafv2:List*",
"workspaces:Describe*"
],
"Resource": "*"
}
]
}
EOF
# Create IAM policy
aws iam create-policy \
--policy-name ProwlerPolicy \
--policy-document file://prowler-policy.json
# Attach policy to user
aws iam attach-user-policy \
--user-name your-username \
--policy-arn arn:aws:iam::ACCOUNT-ID:policy/ProwlerPolicy
# Create IAM role for cross-account access
aws iam create-role \
--role-name ProwlerRole \
--assume-role-policy-document '{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::TRUSTED-ACCOUNT-ID:root"
},
"Action": "sts:AssumeRole"
}
]
}'
# Attach policy to role
aws iam attach-role-policy \
--role-name ProwlerRole \
--policy-arn arn:aws:iam::ACCOUNT-ID:policy/ProwlerPolicy
Basic Usage and Scanning
Simple Scans
# Basic AWS scan (all checks)
prowler aws
# Scan specific region
prowler aws --region us-east-1
# Scan multiple regions
prowler aws --region us-east-1,us-west-2,eu-west-1
# Scan with specific profile
prowler aws --profile production
# Quick scan (essential checks only)
prowler aws --quick
# Scan specific service
prowler aws --service s3
prowler aws --service ec2
prowler aws --service iam
# Scan multiple services
prowler aws --service s3,ec2,iam,vpc
# List available services
prowler aws --list-services
Advanced Scanning Options
# Scan with specific checks
prowler aws --check s3_bucket_public_access_block
prowler aws --check ec2_instance_public_ip
prowler aws --check iam_root_access_key_check
# Scan with check patterns
prowler aws --check "*s3*"
prowler aws --check "*encryption*"
prowler aws --check "*public*"
# Exclude specific checks
prowler aws --excluded-checks s3_bucket_public_access_block
prowler aws --excluded-checks "*logging*"
# Scan with compliance framework
prowler aws --compliance cis_1.5_aws
prowler aws --compliance pci_3.2.1_aws
prowler aws --compliance iso27001_2013_aws
prowler aws --compliance gdpr_aws
prowler aws --compliance hipaa_aws
prowler aws --compliance soc2_aws
# List available compliance frameworks
prowler aws --list-compliance
# Scan with severity filter
prowler aws --severity critical
prowler aws --severity high,critical
prowler aws --severity medium,high,critical
# Scan with custom configuration
prowler aws --config-file custom_config.yaml
# Scan with resource filtering
prowler aws --resource-tags Environment=Production
prowler aws --resource-tags "Owner=SecurityTeam,Environment=Production"
# Scan with output options
prowler aws --output-formats json
prowler aws --output-formats csv,html,json
prowler aws --output-directory ./reports
prowler aws --output-filename custom_report
Multi-Account Scanning
# Scan using assume role
prowler aws --role arn:aws:iam::123456789012:role/ProwlerRole
# Scan multiple accounts
cat > accounts.txt << 'EOF'
123456789012:arn:aws:iam::123456789012:role/ProwlerRole
234567890123:arn:aws:iam::234567890123:role/ProwlerRole
345678901234:arn:aws:iam::345678901234:role/ProwlerRole
EOF
# Scan all accounts in file
prowler aws --organizations-role ProwlerRole
# Create multi-account scanning script
cat > multi_account_scan.sh << 'EOF'
#!/bin/bash
# Multi-account Prowler scanning
ACCOUNTS=(
"123456789012:arn:aws:iam::123456789012:role/ProwlerRole"
"234567890123:arn:aws:iam::234567890123:role/ProwlerRole"
"345678901234:arn:aws:iam::345678901234:role/ProwlerRole"
)
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
OUTPUT_DIR="multi_account_scan_$TIMESTAMP"
mkdir -p "$OUTPUT_DIR"
echo "Starting multi-account Prowler scan..."
for account_info in "${ACCOUNTS[@]}"; do
account_id=$(echo "$account_info" | cut -d':' -f1)
role_arn=$(echo "$account_info" | cut -d':' -f2-)
echo "Scanning account: $account_id"
prowler aws \
--role "$role_arn" \
--output-directory "$OUTPUT_DIR" \
--output-filename "prowler_${account_id}" \
--output-formats json,csv,html
echo "Completed scan for account: $account_id"
done
# Generate summary report
python3 << 'PYTHON'
import json
import glob
import os
def generate_summary(output_dir):
summary = {
"timestamp": "$TIMESTAMP",
"accounts": {},
"total_findings": 0,
"severity_breakdown": {"CRITICAL": 0, "HIGH": 0, "MEDIUM": 0, "LOW": 0, "INFO": 0}
}
for json_file in glob.glob(f"{output_dir}/prowler_*.json"):
account_id = os.path.basename(json_file).replace("prowler_", "").replace(".json", "")
try:
with open(json_file, 'r') as f:
data = json.load(f)
findings = 0
for result in data:
if result.get("Status") == "FAIL":
findings += 1
severity = result.get("Severity", "UNKNOWN")
if severity in summary["severity_breakdown"]:
summary["severity_breakdown"][severity] += 1
summary["accounts"][account_id] = {
"findings": findings,
"total_checks": len(data)
}
summary["total_findings"] += findings
except Exception as e:
print(f"Error processing {json_file}: {e}")
with open(f"{output_dir}/multi_account_summary.json", 'w') as f:
json.dump(summary, f, indent=2)
print(f"Summary report generated: {output_dir}/multi_account_summary.json")
generate_summary("$OUTPUT_DIR")
PYTHON
echo "Multi-account scan completed. Results in: $OUTPUT_DIR"
EOF
chmod +x multi_account_scan.sh
./multi_account_scan.sh
Compliance Framework Scanning
CIS Benchmarks
# CIS AWS Foundations Benchmark v1.5
prowler aws --compliance cis_1.5_aws
# CIS AWS Foundations Benchmark v1.4
prowler aws --compliance cis_1.4_aws
# CIS AWS Foundations Benchmark v1.3
prowler aws --compliance cis_1.3_aws
# CIS Controls v8
prowler aws --compliance cis_controls_v8_aws
# Run specific CIS sections
prowler aws --check "cis_1.*" # Identity and Access Management
prowler aws --check "cis_2.*" # Storage
prowler aws --check "cis_3.*" # Logging
prowler aws --check "cis_4.*" # Monitoring
prowler aws --check "cis_5.*" # Networking
# Generate CIS compliance report
prowler aws \
--compliance cis_1.5_aws \
--output-formats json,html,csv \
--output-directory ./cis_reports \
--output-filename cis_1.5_compliance_report
Industry Standards
# PCI DSS 3.2.1
prowler aws --compliance pci_3.2.1_aws
# ISO 27001:2013
prowler aws --compliance iso27001_2013_aws
# NIST 800-53 Rev 5
prowler aws --compliance nist_800_53_rev5_aws
# NIST 800-171 Rev 2
prowler aws --compliance nist_800_171_rev2_aws
# NIST Cybersecurity Framework
prowler aws --compliance nist_csf_1.1_aws
# SOC 2
prowler aws --compliance soc2_aws
# GDPR
prowler aws --compliance gdpr_aws
# HIPAA
prowler aws --compliance hipaa_aws
# FFIEC
prowler aws --compliance ffiec_aws
# ENS (Esquema Nacional de Seguridad)
prowler aws --compliance ens_rd2022_aws
# AWS Foundational Technical Review (FTR)
prowler aws --compliance aws_foundational_technical_review
# Generate comprehensive compliance report
cat > compliance_scan.sh << 'EOF'
#!/bin/bash
# Comprehensive compliance scanning
FRAMEWORKS=(
"cis_1.5_aws"
"pci_3.2.1_aws"
"iso27001_2013_aws"
"nist_800_53_rev5_aws"
"soc2_aws"
"gdpr_aws"
"hipaa_aws"
)
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
COMPLIANCE_DIR="compliance_reports_$TIMESTAMP"
mkdir -p "$COMPLIANCE_DIR"
echo "Starting comprehensive compliance scanning..."
for framework in "${FRAMEWORKS[@]}"; do
echo "Running $framework compliance scan..."
prowler aws \
--compliance "$framework" \
--output-formats json,html,csv \
--output-directory "$COMPLIANCE_DIR" \
--output-filename "${framework}_report"
echo "Completed $framework scan"
done
# Generate compliance summary
python3 << 'PYTHON'
import json
import glob
import os
def generate_compliance_summary(compliance_dir):
frameworks = [
"cis_1.5_aws", "pci_3.2.1_aws", "iso27001_2013_aws",
"nist_800_53_rev5_aws", "soc2_aws", "gdpr_aws", "hipaa_aws"
]
summary = {
"timestamp": "$TIMESTAMP",
"frameworks": {},
"overall_score": 0
}
total_score = 0
framework_count = 0
for framework in frameworks:
report_file = f"{compliance_dir}/{framework}_report.json"
if os.path.exists(report_file):
with open(report_file, 'r') as f:
data = json.load(f)
total_checks = len(data)
passed_checks = sum(1 for result in data if result.get("Status") == "PASS")
failed_checks = total_checks - passed_checks
score = (passed_checks / total_checks * 100) if total_checks > 0 else 0
summary["frameworks"][framework] = {
"total_checks": total_checks,
"passed_checks": passed_checks,
"failed_checks": failed_checks,
"compliance_score": round(score, 2)
}
total_score += score
framework_count += 1
if framework_count > 0:
summary["overall_score"] = round(total_score / framework_count, 2)
with open(f"{compliance_dir}/compliance_summary.json", 'w') as f:
json.dump(summary, f, indent=2)
print(f"Compliance summary generated: {compliance_dir}/compliance_summary.json")
generate_compliance_summary("$COMPLIANCE_DIR")
PYTHON
echo "Comprehensive compliance scanning completed. Reports in: $COMPLIANCE_DIR"
EOF
chmod +x compliance_scan.sh
./compliance_scan.sh
Custom Compliance Framework
# Create custom compliance framework: custom_framework.yaml
metadata:
name: "Custom Security Framework"
version: "1.0"
description: "Organization-specific security compliance framework"
author: "Security Team"
requirements:
- requirement_id: "CSF-001"
requirement_description: "Data Encryption Requirements"
requirement_attributes:
- name: "Data at Rest Encryption"
description: "All data must be encrypted at rest"
checks:
- "s3_bucket_default_encryption"
- "rds_instance_storage_encrypted"
- "ebs_volume_encryption"
- "efs_encryption_at_rest_enabled"
- requirement_id: "CSF-002"
requirement_description: "Network Security Controls"
requirement_attributes:
- name: "Network Access Control"
description: "Network access must be properly controlled"
checks:
- "ec2_securitygroup_default_restrict_traffic"
- "ec2_securitygroup_not_used"
- "vpc_flow_logs_enabled"
- "ec2_instance_public_ip"
- requirement_id: "CSF-003"
requirement_description: "Identity and Access Management"
requirement_attributes:
- name: "Privileged Access Management"
description: "Privileged access must be controlled and monitored"
checks:
- "iam_root_access_key_check"
- "iam_mfa_enabled_for_root"
- "iam_user_mfa_enabled_console_access"
- "iam_policy_attached_only_to_group_or_roles"
- requirement_id: "CSF-004"
requirement_description: "Logging and Monitoring"
requirement_attributes:
- name: "Security Monitoring"
description: "Security events must be logged and monitored"
checks:
- "cloudtrail_multi_region_enabled"
- "cloudtrail_log_file_validation_enabled"
- "cloudwatch_log_group_retention_policy_specific_days_enabled"
- "guardduty_is_enabled"
- requirement_id: "CSF-005"
requirement_description: "Backup and Recovery"
requirement_attributes:
- name: "Data Protection"
description: "Critical data must be backed up and recoverable"
checks:
- "rds_instance_backup_enabled"
- "dynamodb_point_in_time_recovery_enabled"
- "s3_bucket_versioning_enabled"
- "ec2_ebs_snapshot_encrypted"
# Run custom compliance framework
prowler aws --compliance-file custom_framework.yaml
Custom Checks and Extensions
Creating Custom Checks
#!/usr/bin/env python3
# Custom Prowler check example
"""
Custom check: Ensure EC2 instances have required tags
"""
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.ec2.ec2_client import ec2_client
class ec2_instance_required_tags(Check):
def execute(self):
findings = []
# Required tags for compliance
required_tags = ["Environment", "Owner", "Project", "CostCenter"]
for region in ec2_client.regional_clients:
for instance in ec2_client.instances[region]:
report = Check_Report_AWS(self.metadata())
report.region = region
report.resource_id = instance.id
report.resource_arn = instance.arn
report.resource_tags = instance.tags
# Check if all required tags are present
instance_tag_keys = [tag["Key"] for tag in instance.tags] if instance.tags else []
missing_tags = [tag for tag in required_tags if tag not in instance_tag_keys]
if missing_tags:
report.status = "FAIL"
report.status_extended = f"EC2 instance {instance.id} is missing required tags: {', '.join(missing_tags)}"
else:
report.status = "PASS"
report.status_extended = f"EC2 instance {instance.id} has all required tags"
findings.append(report)
return findings
# Metadata for the check
def metadata():
return {
"CheckID": "ec2_instance_required_tags",
"CheckTitle": "Ensure EC2 instances have required tags",
"CheckType": ["Software and Configuration Checks"],
"ServiceName": "ec2",
"SubServiceName": "",
"ResourceIdTemplate": "arn:aws:ec2:region:account-id:instance/instance-id",
"Severity": "medium",
"ResourceType": "AwsEc2Instance",
"Description": "Ensure EC2 instances have all required tags for compliance and cost management",
"Risk": "Without proper tagging, instances cannot be properly managed, tracked, or billed",
"RelatedUrl": "https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Using_Tags.html",
"Remediation": {
"Code": {
"CLI": "aws ec2 create-tags --resources instance-id --tags Key=TagName,Value=TagValue",
"NativeIaC": "",
"Other": "https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Using_Tags.html",
"Terraform": ""
},
"Recommendation": {
"Text": "Add the required tags to all EC2 instances",
"Url": "https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Using_Tags.html"
}
},
"Categories": ["tagging"],
"DependsOn": [],
"RelatedTo": [],
"Notes": "This check ensures instances have required tags for compliance"
}
Custom Check Configuration
# custom_checks_config.yaml
custom_checks:
ec2_instance_required_tags:
enabled: true
severity: "medium"
required_tags:
- "Environment"
- "Owner"
- "Project"
- "CostCenter"
- "Application"
s3_bucket_naming_convention:
enabled: true
severity: "low"
naming_pattern: "^(dev|staging|prod)-[a-z0-9-]+$"
rds_instance_naming_convention:
enabled: true
severity: "low"
naming_pattern: "^(dev|staging|prod)-[a-z0-9-]+-db$"
security_group_description_required:
enabled: true
severity: "medium"
min_description_length: 10
cloudtrail_custom_requirements:
enabled: true
severity: "high"
required_event_selectors: true
required_insight_selectors: true
required_kms_encryption: true
# Run with custom configuration
prowler aws --config-file custom_checks_config.yaml
Bulk Custom Checks
# Create directory for custom checks
mkdir -p custom_checks/aws/ec2
mkdir -p custom_checks/aws/s3
mkdir -p custom_checks/aws/iam
# Create multiple custom checks
cat > custom_checks/aws/ec2/ec2_instance_approved_amis.py << 'EOF'
#!/usr/bin/env python3
"""
Custom check: Ensure EC2 instances use approved AMIs
"""
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.ec2.ec2_client import ec2_client
class ec2_instance_approved_amis(Check):
def execute(self):
findings = []
# Approved AMI patterns
approved_ami_patterns = [
"ami-0abcdef1234567890", # Approved base AMI
"ami-0123456789abcdef0", # Approved hardened AMI
]
for region in ec2_client.regional_clients:
for instance in ec2_client.instances[region]:
report = Check_Report_AWS(self.metadata())
report.region = region
report.resource_id = instance.id
report.resource_arn = instance.arn
if instance.image_id in approved_ami_patterns:
report.status = "PASS"
report.status_extended = f"EC2 instance {instance.id} uses approved AMI {instance.image_id}"
else:
report.status = "FAIL"
report.status_extended = f"EC2 instance {instance.id} uses unapproved AMI {instance.image_id}"
findings.append(report)
return findings
EOF
cat > custom_checks/aws/s3/s3_bucket_lifecycle_policy.py << 'EOF'
#!/usr/bin/env python3
"""
Custom check: Ensure S3 buckets have lifecycle policies
"""
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.s3.s3_client import s3_client
class s3_bucket_lifecycle_policy(Check):
def execute(self):
findings = []
for bucket in s3_client.buckets:
report = Check_Report_AWS(self.metadata())
report.region = bucket.region
report.resource_id = bucket.name
report.resource_arn = bucket.arn
if bucket.lifecycle_configuration:
report.status = "PASS"
report.status_extended = f"S3 bucket {bucket.name} has lifecycle policy configured"
else:
report.status = "FAIL"
report.status_extended = f"S3 bucket {bucket.name} does not have lifecycle policy configured"
findings.append(report)
return findings
EOF
# Install custom checks
export PYTHONPATH="${PYTHONPATH}:$(pwd)/custom_checks"
# Run with custom checks
prowler aws --checks-folder custom_checks
Advanced Reporting and Analysis
Comprehensive Reporting
#!/usr/bin/env python3
# Advanced Prowler reporting and analysis
import json
import csv
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from datetime import datetime
import argparse
from jinja2 import Template
class ProwlerReporter:
"""Advanced reporting for Prowler scan results"""
def __init__(self):
self.timestamp = datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
def load_prowler_results(self, file_path):
"""Load Prowler scan results from JSON file"""
try:
with open(file_path, 'r') as f:
return json.load(f)
except Exception as e:
print(f"Error loading Prowler results: {e}")
return None
def analyze_results(self, results):
"""Analyze Prowler scan results and generate statistics"""
analysis = {
'total_checks': len(results),
'passed_checks': 0,
'failed_checks': 0,
'manual_checks': 0,
'severity_breakdown': {'CRITICAL': 0, 'HIGH': 0, 'MEDIUM': 0, 'LOW': 0, 'INFO': 0},
'service_breakdown': {},
'region_breakdown': {},
'compliance_breakdown': {},
'top_failures': []
}
for result in results:
status = result.get('Status', 'UNKNOWN')
severity = result.get('Severity', 'UNKNOWN')
service = result.get('ServiceName', 'UNKNOWN')
region = result.get('Region', 'UNKNOWN')
compliance = result.get('Compliance', {})
# Count by status
if status == 'PASS':
analysis['passed_checks'] += 1
elif status == 'FAIL':
analysis['failed_checks'] += 1
elif status == 'MANUAL':
analysis['manual_checks'] += 1
# Count by severity
if severity in analysis['severity_breakdown']:
analysis['severity_breakdown'][severity] += 1
# Count by service
if service not in analysis['service_breakdown']:
analysis['service_breakdown'][service] = {'total': 0, 'failed': 0}
analysis['service_breakdown'][service]['total'] += 1
if status == 'FAIL':
analysis['service_breakdown'][service]['failed'] += 1
# Count by region
if region not in analysis['region_breakdown']:
analysis['region_breakdown'][region] = {'total': 0, 'failed': 0}
analysis['region_breakdown'][region]['total'] += 1
if status == 'FAIL':
analysis['region_breakdown'][region]['failed'] += 1
# Count by compliance framework
for framework, requirements in compliance.items():
if framework not in analysis['compliance_breakdown']:
analysis['compliance_breakdown'][framework] = {'total': 0, 'failed': 0}
analysis['compliance_breakdown'][framework]['total'] += 1
if status == 'FAIL':
analysis['compliance_breakdown'][framework]['failed'] += 1
# Collect failed checks for top failures
if status == 'FAIL':
analysis['top_failures'].append({
'check_id': result.get('CheckID', 'Unknown'),
'check_title': result.get('CheckTitle', 'Unknown'),
'severity': severity,
'service': service,
'region': region,
'resource_id': result.get('ResourceId', 'Unknown')
})
# Sort top failures by severity
severity_order = {'CRITICAL': 4, 'HIGH': 3, 'MEDIUM': 2, 'LOW': 1, 'INFO': 0}
analysis['top_failures'].sort(key=lambda x: severity_order.get(x['severity'], 0), reverse=True)
return analysis
def generate_executive_summary(self, analysis):
"""Generate executive summary"""
total_checks = analysis['total_checks']
failed_checks = analysis['failed_checks']
compliance_score = ((total_checks - failed_checks) / total_checks * 100) if total_checks > 0 else 0
critical_issues = analysis['severity_breakdown']['CRITICAL']
high_issues = analysis['severity_breakdown']['HIGH']
summary = {
'compliance_score': round(compliance_score, 1),
'total_checks': total_checks,
'failed_checks': failed_checks,
'critical_issues': critical_issues,
'high_issues': high_issues,
'risk_level': self._calculate_risk_level(compliance_score, critical_issues, high_issues),
'top_services_at_risk': self._get_top_services_at_risk(analysis['service_breakdown']),
'recommendations': self._generate_recommendations(analysis)
}
return summary
def _calculate_risk_level(self, compliance_score, critical_issues, high_issues):
"""Calculate overall risk level"""
if critical_issues > 0 or compliance_score < 70:
return "HIGH"
elif high_issues > 5 or compliance_score < 85:
return "MEDIUM"
else:
return "LOW"
def _get_top_services_at_risk(self, service_breakdown):
"""Get services with most failures"""
services_at_risk = []
for service, stats in service_breakdown.items():
if stats['failed'] > 0:
failure_rate = (stats['failed'] / stats['total'] * 100) if stats['total'] > 0 else 0
services_at_risk.append({
'service': service,
'failed_checks': stats['failed'],
'total_checks': stats['total'],
'failure_rate': round(failure_rate, 1)
})
return sorted(services_at_risk, key=lambda x: x['failure_rate'], reverse=True)[:5]
def _generate_recommendations(self, analysis):
"""Generate security recommendations"""
recommendations = []
# Critical issues
if analysis['severity_breakdown']['CRITICAL'] > 0:
recommendations.append({
'priority': 'IMMEDIATE',
'title': 'Address Critical Security Issues',
'description': f"Immediately remediate {analysis['severity_breakdown']['CRITICAL']} critical security issues"
})
# High issues
if analysis['severity_breakdown']['HIGH'] > 5:
recommendations.append({
'priority': 'HIGH',
'title': 'Reduce High-Severity Findings',
'description': f"Address {analysis['severity_breakdown']['HIGH']} high-severity security findings"
})
# Service-specific recommendations
for service, stats in analysis['service_breakdown'].items():
if stats['failed'] > 10:
recommendations.append({
'priority': 'MEDIUM',
'title': f'Improve {service.upper()} Security',
'description': f"Focus on {service} service with {stats['failed']} failed checks"
})
return recommendations[:5] # Top 5 recommendations
def generate_detailed_report(self, analysis, results, output_file):
"""Generate detailed HTML report"""
executive_summary = self.generate_executive_summary(analysis)
html_template = """
<!DOCTYPE html>
<html>
<head>
<title>Prowler Security Assessment Report - {{ timestamp }}</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; line-height: 1.6; }
.header { background: #2c3e50; color: white; padding: 20px; border-radius: 5px; margin-bottom: 20px; }
.executive-summary { background: #ecf0f1; padding: 20px; border-radius: 5px; margin-bottom: 20px; }
.metrics { display: flex; justify-content: space-around; margin: 20px 0; }
.metric { text-align: center; padding: 15px; border: 1px solid #ddd; border-radius: 5px; }
.metric h3 { margin: 0; color: #2c3e50; }
.metric .value { font-size: 2em; font-weight: bold; }
.risk-high { color: #e74c3c; }
.risk-medium { color: #f39c12; }
.risk-low { color: #27ae60; }
.critical { color: #c0392b; }
.high { color: #e67e22; }
.medium { color: #f1c40f; }
.low { color: #2ecc71; }
.section { margin: 30px 0; }
.chart { text-align: center; margin: 20px 0; }
table { border-collapse: collapse; width: 100%; margin: 20px 0; }
th, td { border: 1px solid #ddd; padding: 12px; text-align: left; }
th { background-color: #34495e; color: white; }
.finding-row { border-left: 4px solid; }
.finding-critical { border-left-color: #c0392b; }
.finding-high { border-left-color: #e67e22; }
.finding-medium { border-left-color: #f1c40f; }
.finding-low { border-left-color: #2ecc71; }
.recommendation { background: #d5dbdb; padding: 15px; margin: 10px 0; border-radius: 5px; }
.recommendation h4 { margin: 0 0 10px 0; }
</style>
</head>
<body>
<div class="header">
<h1>🔒 Prowler Security Assessment Report</h1>
<p>Generated on: {{ timestamp }}</p>
<p>Account: {{ account_id }}</p>
</div>
<div class="executive-summary">
<h2>Executive Summary</h2>
<div class="metrics">
<div class="metric">
<h3>Compliance Score</h3>
<div class="value risk-{{ executive_summary.risk_level.lower() }}">{{ executive_summary.compliance_score }}%</div>
</div>
<div class="metric">
<h3>Total Checks</h3>
<div class="value">{{ executive_summary.total_checks }}</div>
</div>
<div class="metric">
<h3>Failed Checks</h3>
<div class="value risk-high">{{ executive_summary.failed_checks }}</div>
</div>
<div class="metric">
<h3>Critical Issues</h3>
<div class="value critical">{{ executive_summary.critical_issues }}</div>
</div>
<div class="metric">
<h3>Risk Level</h3>
<div class="value risk-{{ executive_summary.risk_level.lower() }}">{{ executive_summary.risk_level }}</div>
</div>
</div>
</div>
<div class="section">
<h2>Key Recommendations</h2>
{% for recommendation in executive_summary.recommendations %}
<div class="recommendation">
<h4>{{ recommendation.priority }}: {{ recommendation.title }}</h4>
<p>{{ recommendation.description }}</p>
</div>
{% endfor %}
</div>
<div class="section">
<h2>Severity Breakdown</h2>
<table>
<tr><th>Severity</th><th>Count</th><th>Percentage</th></tr>
{% for severity, count in analysis.severity_breakdown.items() %}
<tr class="{{ severity.lower() }}">
<td>{{ severity }}</td>
<td>{{ count }}</td>
<td>{{ "%.1f"|format((count / analysis.total_checks * 100) if analysis.total_checks > 0 else 0) }}%</td>
</tr>
{% endfor %}
</table>
</div>
<div class="section">
<h2>Services at Risk</h2>
<table>
<tr><th>Service</th><th>Failed Checks</th><th>Total Checks</th><th>Failure Rate</th></tr>
{% for service in executive_summary.top_services_at_risk %}
<tr>
<td>{{ service.service }}</td>
<td>{{ service.failed_checks }}</td>
<td>{{ service.total_checks }}</td>
<td>{{ service.failure_rate }}%</td>
</tr>
{% endfor %}
</table>
</div>
<div class="section">
<h2>Top Security Findings</h2>
<table>
<tr><th>Check</th><th>Severity</th><th>Service</th><th>Region</th><th>Resource</th></tr>
{% for finding in analysis.top_failures[:20] %}
<tr class="finding-row finding-{{ finding.severity.lower() }}">
<td>{{ finding.check_title }}</td>
<td>{{ finding.severity }}</td>
<td>{{ finding.service }}</td>
<td>{{ finding.region }}</td>
<td>{{ finding.resource_id }}</td>
</tr>
{% endfor %}
</table>
</div>
<div class="section">
<h2>Regional Analysis</h2>
<table>
<tr><th>Region</th><th>Total Checks</th><th>Failed Checks</th><th>Success Rate</th></tr>
{% for region, stats in analysis.region_breakdown.items() %}
<tr>
<td>{{ region }}</td>
<td>{{ stats.total }}</td>
<td>{{ stats.failed }}</td>
<td>{{ "%.1f"|format(((stats.total - stats.failed) / stats.total * 100) if stats.total > 0 else 0) }}%</td>
</tr>
{% endfor %}
</table>
</div>
</body>
</html>
"""
template = Template(html_template)
html_content = template.render(
timestamp=self.timestamp,
account_id="123456789012", # Replace with actual account ID
analysis=analysis,
executive_summary=executive_summary
)
with open(output_file, 'w') as f:
f.write(html_content)
print(f"Detailed HTML report generated: {output_file}")
return output_file
def generate_charts(self, analysis, output_dir):
"""Generate charts and visualizations"""
import os
os.makedirs(output_dir, exist_ok=True)
# Set style
plt.style.use('seaborn-v0_8')
# 1. Severity breakdown pie chart
plt.figure(figsize=(15, 10))
# Filter out zero values
severity_data = {k: v for k, v in analysis['severity_breakdown'].items() if v > 0}
if severity_data:
plt.subplot(2, 3, 1)
colors = ['#c0392b', '#e67e22', '#f1c40f', '#2ecc71', '#3498db']
plt.pie(severity_data.values(), labels=severity_data.keys(), autopct='%1.1f%%', colors=colors)
plt.title('Findings by Severity')
# 2. Pass/Fail breakdown
plt.subplot(2, 3, 2)
status_data = [analysis['passed_checks'], analysis['failed_checks']]
status_labels = ['Passed', 'Failed']
colors = ['#27ae60', '#e74c3c']
plt.pie(status_data, labels=status_labels, colors=colors, autopct='%1.1f%%')
plt.title('Overall Compliance Status')
# 3. Top services by failure count
plt.subplot(2, 3, 3)
services = list(analysis['service_breakdown'].keys())[:10]
failures = [analysis['service_breakdown'][s]['failed'] for s in services]
plt.barh(services, failures, color='#e74c3c')
plt.title('Top Services by Failed Checks')
plt.xlabel('Failed Checks')
# 4. Regional analysis
plt.subplot(2, 3, 4)
regions = list(analysis['region_breakdown'].keys())[:10]
region_failures = [analysis['region_breakdown'][r]['failed'] for r in regions]
plt.bar(regions, region_failures, color='#f39c12')
plt.title('Failed Checks by Region')
plt.ylabel('Failed Checks')
plt.xticks(rotation=45)
# 5. Compliance score gauge
plt.subplot(2, 3, 5)
total_checks = analysis['total_checks']
passed_checks = analysis['passed_checks']
compliance_score = (passed_checks / total_checks * 100) if total_checks > 0 else 0
# Create gauge chart
fig, ax = plt.subplots(figsize=(6, 6), subplot_kw=dict(projection='polar'))
theta = compliance_score * 2 * 3.14159 / 100
ax.barh(0, theta, height=0.5, color='#27ae60' if compliance_score > 80 else '#f39c12' if compliance_score > 60 else '#e74c3c')
ax.set_ylim(-0.5, 0.5)
ax.set_title(f'Compliance Score: {compliance_score:.1f}%')
# 6. Trend analysis (if historical data available)
plt.subplot(2, 3, 6)
# Placeholder for trend data
months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun']
scores = [75, 78, 82, 85, 87, 90] # Example data
plt.plot(months, scores, marker='o', color='#3498db')
plt.title('Compliance Score Trend')
plt.ylabel('Compliance Score (%)')
plt.ylim(0, 100)
plt.tight_layout()
chart_file = f"{output_dir}/prowler_security_analysis.png"
plt.savefig(chart_file, dpi=300, bbox_inches='tight')
plt.close()
print(f"Charts generated: {chart_file}")
return chart_file
def main():
parser = argparse.ArgumentParser(description='Prowler Advanced Reporter')
parser.add_argument('prowler_file', help='Prowler scan results JSON file')
parser.add_argument('--output-dir', default='reports', help='Output directory for reports')
parser.add_argument('--charts', action='store_true', help='Generate charts and visualizations')
args = parser.parse_args()
reporter = ProwlerReporter()
# Load Prowler results
prowler_results = reporter.load_prowler_results(args.prowler_file)
if not prowler_results:
return
# Analyze results
analysis = reporter.analyze_results(prowler_results)
# Create output directory
import os
os.makedirs(args.output_dir, exist_ok=True)
# Generate reports
report_files = []
# HTML report
html_file = f"{args.output_dir}/prowler_security_report_{reporter.timestamp}.html"
reporter.generate_detailed_report(analysis, prowler_results, html_file)
report_files.append(html_file)
# Charts
if args.charts:
chart_file = reporter.generate_charts(analysis, args.output_dir)
report_files.append(chart_file)
print(f"Report generation completed. Files: {report_files}")
if __name__ == "__main__":
main()
CI/CD Integration and Automation
GitHub Actions Integration
# .github/workflows/prowler-security-scan.yml
name: Prowler Security Scan
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
schedule:
# Run daily at 2 AM UTC
- cron: '0 2 * * *'
workflow_dispatch:
inputs:
compliance_framework:
description: 'Compliance framework to scan'
required: false
default: 'cis_1.5_aws'
type: choice
options:
- cis_1.5_aws
- pci_3.2.1_aws
- iso27001_2013_aws
- nist_800_53_rev5_aws
jobs:
prowler-scan:
runs-on: ubuntu-latest
strategy:
matrix:
region: [us-east-1, us-west-2, eu-west-1]
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: '3.9'
- name: Install Prowler
run: |
pip install prowler
prowler --version
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v2
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ matrix.region }}
- name: Run Prowler scan
run: |
mkdir -p reports
# Determine compliance framework
COMPLIANCE_FRAMEWORK="${{ github.event.inputs.compliance_framework || 'cis_1.5_aws' }}"
# Run Prowler scan
prowler aws \
--region ${{ matrix.region }} \
--compliance $COMPLIANCE_FRAMEWORK \
--output-formats json,csv,html \
--output-directory reports \
--output-filename prowler_${{ matrix.region }}_scan
- name: Generate security report
run: |
pip install jinja2 matplotlib seaborn pandas
python scripts/prowler_reporter.py reports/prowler_${{ matrix.region }}_scan.json \
--output-dir reports \
--charts
- name: Upload scan results
uses: actions/upload-artifact@v3
with:
name: prowler-scan-results-${{ matrix.region }}
path: reports/
- name: Security gate check
run: |
python << 'EOF'
import json
import sys
with open('reports/prowler_${{ matrix.region }}_scan.json', 'r') as f:
results = json.load(f)
critical_issues = sum(1 for r in results if r.get('Status') == 'FAIL' and r.get('Severity') == 'CRITICAL')
high_issues = sum(1 for r in results if r.get('Status') == 'FAIL' and r.get('Severity') == 'HIGH')
print(f"Critical issues: {critical_issues}")
print(f"High issues: {high_issues}")
# Fail build if critical issues found
if critical_issues > 0:
print("❌ CRITICAL SECURITY ISSUES FOUND!")
sys.exit(1)
# Warn if too many high issues
if high_issues > 10:
print("⚠️ WARNING: High number of high-severity issues found!")
sys.exit(1)
print("✅ Security gate passed")
EOF
- name: Comment PR with results
if: github.event_name == 'pull_request'
uses: actions/github-script@v6
with:
script: |
const fs = require('fs');
const results = JSON.parse(fs.readFileSync('reports/prowler_${{ matrix.region }}_scan.json', 'utf8'));
const totalChecks = results.length;
const failedChecks = results.filter(r => r.Status === 'FAIL').length;
const passedChecks = totalChecks - failedChecks;
const complianceScore = ((passedChecks / totalChecks) * 100).toFixed(1);
const criticalIssues = results.filter(r => r.Status === 'FAIL' && r.Severity === 'CRITICAL').length;
const highIssues = results.filter(r => r.Status === 'FAIL' && r.Severity === 'HIGH').length;
const comment = `## 🔒 Prowler Security Scan Results (${{ matrix.region }})
**Compliance Score:** ${complianceScore}%
**Summary:**
- ✅ Passed: ${passedChecks}
- ❌ Failed: ${failedChecks}
- 🔴 Critical: ${criticalIssues}
- 🟠 High: ${highIssues}
${criticalIssues > 0 ? '⚠️ **Critical security issues found! Please review and remediate.**' : '✅ No critical security issues found.'}
[View detailed report](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})`;
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: comment
});
consolidate-results:
needs: prowler-scan
runs-on: ubuntu-latest
if: always()
steps:
- name: Download all artifacts
uses: actions/download-artifact@v3
- name: Consolidate results
run: |
mkdir -p consolidated_reports
# Combine all regional results
python << 'EOF'
import json
import glob
import os
all_results = []
for result_file in glob.glob('prowler-scan-results-*/prowler_*_scan.json'):
try:
with open(result_file, 'r') as f:
data = json.load(f)
all_results.extend(data)
except Exception as e:
print(f"Error processing {result_file}: {e}")
# Save consolidated results
with open('consolidated_reports/prowler_consolidated_scan.json', 'w') as f:
json.dump(all_results, f, indent=2)
print(f"Consolidated {len(all_results)} results from all regions")
EOF
- name: Generate consolidated report
run: |
pip install jinja2 matplotlib seaborn pandas
python scripts/prowler_reporter.py consolidated_reports/prowler_consolidated_scan.json \
--output-dir consolidated_reports \
--charts
- name: Upload consolidated results
uses: actions/upload-artifact@v3
with:
name: prowler-consolidated-results
path: consolidated_reports/
Jenkins Pipeline
// Jenkinsfile for Prowler security scanning
pipeline {
agent any
parameters {
choice(
name: 'COMPLIANCE_FRAMEWORK',
choices: ['cis_1.5_aws', 'pci_3.2.1_aws', 'iso27001_2013_aws', 'nist_800_53_rev5_aws'],
description: 'Compliance framework to scan'
)
string(
name: 'AWS_REGIONS',
defaultValue: 'us-east-1,us-west-2,eu-west-1',
description: 'AWS regions to scan (comma-separated)'
)
booleanParam(
name: 'GENERATE_CHARTS',
defaultValue: true,
description: 'Generate charts and visualizations'
)
}
environment {
PROWLER_VERSION = '3.12.0'
REPORTS_DIR = 'prowler_reports'
TIMESTAMP = sh(script: 'date +%Y%m%d_%H%M%S', returnStdout: true).trim()
}
stages {
stage('Setup') {
steps {
script {
// Clean workspace
deleteDir()
// Install Prowler
sh '''
python3 -m pip install --upgrade pip
pip3 install prowler==${PROWLER_VERSION}
prowler --version
'''
}
}
}
stage('Security Scan') {
parallel {
stage('Multi-Region Scan') {
steps {
script {
def regions = params.AWS_REGIONS.split(',')
def scanJobs = [:]
regions.each { region ->
scanJobs["Scan ${region}"] = {
withCredentials([
[$class: 'AmazonWebServicesCredentialsBinding',
credentialsId: 'aws-credentials']
]) {
sh """
mkdir -p ${REPORTS_DIR}
prowler aws \\
--region ${region.trim()} \\
--compliance ${params.COMPLIANCE_FRAMEWORK} \\
--output-formats json,csv,html \\
--output-directory ${REPORTS_DIR} \\
--output-filename prowler_${region.trim()}_${TIMESTAMP}
"""
}
}
}
parallel scanJobs
}
}
}
}
}
stage('Generate Reports') {
steps {
script {
sh '''
# Install reporting dependencies
pip3 install jinja2 matplotlib seaborn pandas
# Generate reports for each region
for scan_file in ${REPORTS_DIR}/prowler_*_${TIMESTAMP}.json; do
if [ -f "$scan_file" ]; then
echo "Generating report for $scan_file"
python3 scripts/prowler_reporter.py "$scan_file" \\
--output-dir "${REPORTS_DIR}" \\
${params.GENERATE_CHARTS ? '--charts' : ''}
fi
done
# Consolidate all results
python3 << 'EOF'
import json
import glob
all_results = []
for result_file in glob.glob("${REPORTS_DIR}/prowler_*_${TIMESTAMP}.json"):
try:
with open(result_file, 'r') as f:
data = json.load(f)
all_results.extend(data)
except Exception as e:
print(f"Error processing {result_file}: {e}")
# Save consolidated results
with open("${REPORTS_DIR}/prowler_consolidated_${TIMESTAMP}.json", 'w') as f:
json.dump(all_results, f, indent=2)
print(f"Consolidated {len(all_results)} results")
EOF
# Generate consolidated report
python3 scripts/prowler_reporter.py "${REPORTS_DIR}/prowler_consolidated_${TIMESTAMP}.json" \\
--output-dir "${REPORTS_DIR}" \\
${params.GENERATE_CHARTS ? '--charts' : ''}
'''
}
}
}
stage('Security Gate') {
steps {
script {
sh '''
python3 << 'EOF'
import json
import sys
# Load consolidated results
with open("${REPORTS_DIR}/prowler_consolidated_${TIMESTAMP}.json", 'r') as f:
results = json.load(f)
# Count issues by severity
critical_issues = sum(1 for r in results if r.get("Status") == "FAIL" and r.get("Severity") == "CRITICAL")
high_issues = sum(1 for r in results if r.get("Status") == "FAIL" and r.get("Severity") == "HIGH")
medium_issues = sum(1 for r in results if r.get("Status") == "FAIL" and r.get("Severity") == "MEDIUM")
print(f"Security Assessment Results:")
print(f"Critical issues: {critical_issues}")
print(f"High issues: {high_issues}")
print(f"Medium issues: {medium_issues}")
# Security gate logic
if critical_issues > 0:
print("❌ CRITICAL SECURITY ISSUES FOUND!")
print("Build failed due to critical security issues.")
sys.exit(1)
if high_issues > 15:
print("⚠️ WARNING: High number of high-severity issues found!")
print("Consider addressing high-severity issues.")
sys.exit(1)
print("✅ Security gate passed")
EOF
'''
}
}
}
stage('Archive Results') {
steps {
archiveArtifacts artifacts: "${REPORTS_DIR}/**/*", fingerprint: true
publishHTML([
allowMissing: false,
alwaysLinkToLastBuild: true,
keepAll: true,
reportDir: env.REPORTS_DIR,
reportFiles: '*.html',
reportName: 'Prowler Security Report'
])
}
}
stage('Notify') {
steps {
script {
// Send Slack notification
def consolidatedResults = readJSON file: "${env.REPORTS_DIR}/prowler_consolidated_${env.TIMESTAMP}.json"
def totalChecks = consolidatedResults.size()
def failedChecks = consolidatedResults.count { it.Status == 'FAIL' }
def complianceScore = ((totalChecks - failedChecks) / totalChecks * 100).round(1)
def criticalIssues = consolidatedResults.count { it.Status == 'FAIL' && it.Severity == 'CRITICAL' }
def highIssues = consolidatedResults.count { it.Status == 'FAIL' && it.Severity == 'HIGH' }
slackSend(
channel: '#security',
color: complianceScore > 90 ? 'good' : complianceScore > 70 ? 'warning' : 'danger',
message: """
🔒 Prowler Security Scan Completed
Project: ${env.JOB_NAME}
Build: ${env.BUILD_NUMBER}
Framework: ${params.COMPLIANCE_FRAMEWORK}
Compliance Score: ${complianceScore}%
Critical Issues: ${criticalIssues}
High Issues: ${highIssues}
Failed Checks: ${failedChecks}/${totalChecks}
Report: ${env.BUILD_URL}Prowler_Security_Report/
""".trim()
)
}
}
}
}
post {
always {
// Clean up large files but keep reports
sh "find . -name '*.json' -size +50M -delete"
}
failure {
emailext(
subject: "Prowler Security Scan Failed - ${env.JOB_NAME} #${env.BUILD_NUMBER}",
body: """
The Prowler security scan has failed.
Project: ${env.JOB_NAME}
Build Number: ${env.BUILD_NUMBER}
Framework: ${params.COMPLIANCE_FRAMEWORK}
Build URL: ${env.BUILD_URL}
Please check the build logs and security report for more details.
""",
to: "${env.CHANGE_AUTHOR_EMAIL ?: 'security-team@company.com'}"
)
}
}
}
Performance Optimization and Troubleshooting
Performance Optimization
#!/bin/bash
# Prowler performance optimization
optimize_prowler_performance() {
echo "Optimizing Prowler performance..."
# 1. Create performance configuration
cat > prowler_performance_config.yaml << 'EOF'
# Prowler performance configuration
# Global settings
global:
# Increase timeout for slow checks
timeout: 300
# Parallel execution settings
max_workers: 10
# Memory optimization
max_memory_usage: "4GB"
# Cache settings
enable_cache: true
cache_timeout: 3600
# Service-specific optimizations
services:
# EC2 optimizations
ec2:
enabled: true
timeout: 120
max_parallel_requests: 5
# S3 optimizations
s3:
enabled: true
timeout: 180
max_parallel_requests: 3
# IAM optimizations
iam:
enabled: true
timeout: 60
max_parallel_requests: 2
# Check-specific optimizations
checks:
# Disable slow or problematic checks
disabled_checks:
- "cloudtrail_cloudwatch_logging_enabled" # Slow check
- "ec2_elastic_ip_shodan" # External dependency
- "s3_bucket_object_versioning" # Can be slow for large buckets
# Check timeouts
check_timeouts:
"iam_user_hardware_mfa_enabled": 30
"ec2_instance_older_than_specific_days": 60
"s3_bucket_public_access_block": 45
# Region optimization
regions:
# Limit to essential regions for faster scanning
enabled_regions:
- "us-east-1"
- "us-west-2"
- "eu-west-1"
- "ap-southeast-1"
# Skip regions with no resources
skip_empty_regions: true
# Output optimization
output:
# Reduce output verbosity for performance
verbose: false
# Compress large outputs
compress_outputs: true
# Limit output formats for faster processing
formats: ["json"]
EOF
# 2. Create optimized scanning script
cat > optimized_prowler_scan.sh << 'EOF'
#!/bin/bash
# Optimized Prowler scanning script
# Performance environment variables
export PYTHONUNBUFFERED=1
export PYTHONDONTWRITEBYTECODE=1
# Memory optimization
export PROWLER_MAX_MEMORY=4096
export PROWLER_CACHE_ENABLED=true
# Parallel processing
export PROWLER_MAX_WORKERS=10
# Function to run optimized scan
run_optimized_scan() {
local region="$1"
local service="$2"
local output_dir="$3"
echo "Running optimized scan for $service in $region..."
# Use timeout to prevent hanging
timeout 1800 prowler aws \
--region "$region" \
--service "$service" \
--config-file prowler_performance_config.yaml \
--output-formats json \
--output-directory "$output_dir" \
--output-filename "prowler_${region}_${service}" \
--quiet
local exit_code=$?
if [ $exit_code -eq 124 ]; then
echo "Warning: Scan for $service in $region timed out"
elif [ $exit_code -ne 0 ]; then
echo "Error: Scan for $service in $region failed with exit code $exit_code"
else
echo "Completed scan for $service in $region"
fi
return $exit_code
}
# Main optimization function
main() {
local timestamp=$(date +%Y%m%d_%H%M%S)
local output_dir="optimized_scan_$timestamp"
mkdir -p "$output_dir"
# Essential services for quick scan
local services=("iam" "s3" "ec2" "vpc" "cloudtrail")
local regions=("us-east-1" "us-west-2" "eu-west-1")
echo "Starting optimized Prowler scan..."
# Parallel scanning by service and region
for service in "${services[@]}"; do
for region in "${regions[@]}"; do
run_optimized_scan "$region" "$service" "$output_dir" &
# Limit concurrent jobs
while [ $(jobs -r | wc -l) -ge 5 ]; do
sleep 5
done
done
done
# Wait for all background jobs to complete
wait
echo "Optimized scan completed. Results in: $output_dir"
# Combine results
python3 << 'PYTHON'
import json
import glob
import os
output_dir = "$output_dir"
combined_results = []
for result_file in glob.glob(f"{output_dir}/prowler_*.json"):
try:
with open(result_file, 'r') as f:
data = json.load(f)
combined_results.extend(data)
print(f"Processed {len(data)} results from {result_file}")
except Exception as e:
print(f"Error processing {result_file}: {e}")
# Save combined results
with open(f"{output_dir}/prowler_combined_optimized.json", 'w') as f:
json.dump(combined_results, f, indent=2)
print(f"Combined {len(combined_results)} total results")
PYTHON
}
# Run optimization
main "$@"
EOF
chmod +x optimized_prowler_scan.sh
echo "Performance optimization setup complete"
}
# Memory optimization
optimize_memory_usage() {
echo "Optimizing Prowler memory usage..."
# Create memory monitoring script
cat > monitor_prowler_memory.sh << 'EOF'
#!/bin/bash
# Monitor Prowler memory usage
PROWLER_PID=""
MEMORY_LOG="prowler_memory.log"
CPU_LOG="prowler_cpu.log"
# Function to monitor resources
monitor_resources() {
echo "Timestamp,Memory_MB,CPU_Percent" > "$MEMORY_LOG"
echo "Timestamp,CPU_Percent" > "$CPU_LOG"
while true; do
if [ -n "$PROWLER_PID" ] && kill -0 "$PROWLER_PID" 2>/dev/null; then
timestamp=$(date '+%Y-%m-%d %H:%M:%S')
# Memory usage in MB
memory_kb=$(ps -p "$PROWLER_PID" -o rss --no-headers 2>/dev/null)
if [ -n "$memory_kb" ]; then
memory_mb=$((memory_kb / 1024))
echo "$timestamp,$memory_mb" >> "$MEMORY_LOG"
fi
# CPU usage
cpu_percent=$(ps -p "$PROWLER_PID" -o %cpu --no-headers 2>/dev/null)
if [ -n "$cpu_percent" ]; then
echo "$timestamp,$cpu_percent" >> "$CPU_LOG"
fi
else
break
fi
sleep 5
done
}
# Start monitoring in background
monitor_resources &
MONITOR_PID=$!
# Run Prowler with memory optimization
export PYTHONUNBUFFERED=1
export PYTHONDONTWRITEBYTECODE=1
# Start Prowler and capture PID
prowler aws --region us-east-1 --service iam --output-formats json &
PROWLER_PID=$!
# Wait for Prowler to complete
wait $PROWLER_PID
# Stop monitoring
kill $MONITOR_PID 2>/dev/null
# Analyze memory usage
python3 << 'PYTHON'
import pandas as pd
import matplotlib.pyplot as plt
try:
# Read memory data
memory_df = pd.read_csv('prowler_memory.log')
memory_df['Timestamp'] = pd.to_datetime(memory_df['Timestamp'])
# Create memory usage chart
plt.figure(figsize=(12, 6))
plt.plot(memory_df['Timestamp'], memory_df['Memory_MB'])
plt.title('Prowler Memory Usage Over Time')
plt.xlabel('Time')
plt.ylabel('Memory Usage (MB)')
plt.xticks(rotation=45)
plt.grid(True)
plt.tight_layout()
plt.savefig('prowler_memory_usage.png')
# Print statistics
print(f"Average Memory Usage: {memory_df['Memory_MB'].mean():.1f} MB")
print(f"Peak Memory Usage: {memory_df['Memory_MB'].max():.1f} MB")
print(f"Memory Usage Range: {memory_df['Memory_MB'].min():.1f} - {memory_df['Memory_MB'].max():.1f} MB")
except Exception as e:
print(f"Error analyzing memory usage: {e}")
PYTHON
echo "Memory monitoring completed"
EOF
chmod +x monitor_prowler_memory.sh
echo "Memory optimization complete"
}
# Run optimizations
optimize_prowler_performance
optimize_memory_usage
Troubleshooting Guide
#!/bin/bash
# Prowler troubleshooting guide
troubleshoot_prowler() {
echo "Prowler Troubleshooting Guide"
echo "============================="
# Check Python installation
if ! command -v python3 &> /dev/null; then
echo "❌ Python 3 not found"
echo "Solution: Install Python 3.9 or later"
echo " sudo apt update && sudo apt install python3 python3-pip"
return 1
fi
python_version=$(python3 --version | cut -d' ' -f2)
echo "✅ Python found: $python_version"
# Check Prowler installation
if ! command -v prowler &> /dev/null; then
echo "❌ Prowler not found"
echo "Solution: Install Prowler"
echo " pip3 install prowler"
return 1
fi
prowler_version=$(prowler --version 2>&1 | head -n1)
echo "✅ Prowler found: $prowler_version"
# Check AWS CLI installation
if ! command -v aws &> /dev/null; then
echo "⚠️ AWS CLI not found"
echo "Solution: Install AWS CLI"
echo " curl 'https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip' -o 'awscliv2.zip'"
echo " unzip awscliv2.zip && sudo ./aws/install"
else
aws_version=$(aws --version 2>&1)
echo "✅ AWS CLI found: $aws_version"
fi
# Check AWS credentials
echo ""
echo "Checking AWS credentials..."
if aws sts get-caller-identity > /dev/null 2>&1; then
account_id=$(aws sts get-caller-identity --query Account --output text)
user_arn=$(aws sts get-caller-identity --query Arn --output text)
echo "✅ AWS credentials configured"
echo " Account ID: $account_id"
echo " User/Role: $user_arn"
else
echo "❌ AWS credentials not configured or invalid"
echo "Solution: Configure AWS credentials"
echo " aws configure"
echo " or set environment variables:"
echo " export AWS_ACCESS_KEY_ID=your-key"
echo " export AWS_SECRET_ACCESS_KEY=your-secret"
echo " export AWS_DEFAULT_REGION=us-east-1"
fi
# Test basic functionality
echo ""
echo "Testing basic functionality..."
if prowler --help > /dev/null 2>&1; then
echo "✅ Prowler help command works"
else
echo "❌ Prowler help command failed"
echo "Solution: Reinstall Prowler"
echo " pip3 uninstall prowler && pip3 install prowler"
fi
# Check permissions
echo ""
echo "Checking AWS permissions..."
# Test basic permissions
if aws iam get-account-summary > /dev/null 2>&1; then
echo "✅ IAM read permissions available"
else
echo "⚠️ IAM read permissions not available"
echo "Solution: Ensure IAM permissions are configured"
fi
if aws s3 ls > /dev/null 2>&1; then
echo "✅ S3 read permissions available"
else
echo "⚠️ S3 read permissions not available"
echo "Solution: Ensure S3 permissions are configured"
fi
if aws ec2 describe-regions > /dev/null 2>&1; then
echo "✅ EC2 read permissions available"
else
echo "⚠️ EC2 read permissions not available"
echo "Solution: Ensure EC2 permissions are configured"
fi
# Check system resources
echo ""
echo "Checking system resources..."
available_memory=$(free -m | awk 'NR==2{printf "%.1f", $7/1024}')
if (( $(echo "$available_memory < 2.0" | bc -l) )); then
echo "⚠️ Low available memory: ${available_memory}GB"
echo "Recommendation: Ensure at least 4GB available memory for large scans"
else
echo "✅ Available memory: ${available_memory}GB"
fi
# Check disk space
disk_usage=$(df . | tail -1 | awk '{print $5}' | sed 's/%//')
if [ "$disk_usage" -gt 90 ]; then
echo "⚠️ High disk usage: ${disk_usage}%"
echo "Solution: Free up disk space"
else
echo "✅ Disk usage: ${disk_usage}%"
fi
echo ""
echo "Troubleshooting completed"
}
# Common error solutions
fix_common_errors() {
echo "Common Prowler Errors and Solutions"
echo "==================================="
cat << 'EOF'
1. "ModuleNotFoundError: No module named 'prowler'"
Solution:
- Install Prowler: pip3 install prowler
- Check Python path: python3 -c "import sys; print(sys.path)"
2. "AWS credentials not configured"
Solution:
- Run: aws configure
- Or set environment variables:
export AWS_ACCESS_KEY_ID=your-key
export AWS_SECRET_ACCESS_KEY=your-secret
3. "AccessDenied" or "UnauthorizedOperation"
Solution:
- Check IAM permissions
- Ensure user/role has required policies attached
- Use Prowler IAM policy from documentation
4. "Timeout" or "Connection timeout"
Solution:
- Increase timeout: --timeout 300
- Check internet connectivity
- Reduce parallel requests
5. "Memory allocation failed" or "Out of memory"
Solution:
- Scan fewer regions: --region us-east-1
- Scan specific services: --service s3
- Increase system memory
6. "Check failed" or "Plugin error"
Solution:
- Update Prowler: pip3 install --upgrade prowler
- Skip problematic checks: --excluded-checks check-name
- Check Prowler logs for details
7. "JSON decode error" in output
Solution:
- Use single output format: --output-formats json
- Check for mixed output in logs
- Ensure clean JSON output
8. "Rate limiting" or "API throttling"
Solution:
- Reduce parallel requests
- Add delays between API calls
- Use multiple AWS accounts/regions
9. "SSL/TLS certificate errors"
Solution:
- Update certificates: sudo apt update && sudo apt install ca-certificates
- Check system time: timedatectl status
- Update Python requests: pip3 install --upgrade requests
10. "Scan takes too long" or "Hangs"
Solution:
- Use region filtering: --region us-east-1
- Scan specific services: --service iam,s3
- Use quick scan: --quick
- Monitor with timeout: timeout 3600 prowler aws
EOF
}
# Performance diagnostics
diagnose_performance() {
echo "Diagnosing Prowler Performance"
echo "=============================="
# Test scan performance
echo "Running performance test..."
start_time=$(date +%s.%N)
# Run a simple scan
timeout 120 prowler aws --region us-east-1 --service iam --check iam_root_access_key_check --output-formats json > /dev/null 2>&1
exit_code=$?
end_time=$(date +%s.%N)
duration=$(echo "$end_time - $start_time" | bc)
if [ $exit_code -eq 0 ]; then
echo "✅ Performance test completed in ${duration}s"
elif [ $exit_code -eq 124 ]; then
echo "⚠️ Performance test timed out (>120s)"
echo "Recommendation: Check network connectivity and AWS API performance"
else
echo "❌ Performance test failed"
echo "Recommendation: Check configuration and credentials"
fi
# Check Python performance
python_startup_time=$(python3 -c "import time; start=time.time(); import prowler; print(f'{time.time()-start:.2f}')" 2>/dev/null || echo "N/A")
echo "Python import time: ${python_startup_time}s"
# System load
load_avg=$(uptime | awk -F'load average:' '{print $2}' | awk '{print $1}' | sed 's/,//')
echo "System load average: $load_avg"
# Network connectivity test
echo "Testing AWS API connectivity..."
aws_api_time=$(time (aws sts get-caller-identity > /dev/null 2>&1) 2>&1 | grep real | awk '{print $2}')
echo "AWS API response time: $aws_api_time"
# Recommendations
echo ""
echo "Performance Recommendations:"
echo "- Use region filtering for faster scans"
echo "- Scan specific services instead of all services"
echo "- Use --quick for essential checks only"
echo "- Increase system memory for large environments"
echo "- Use SSD storage for better I/O performance"
echo "- Monitor AWS API rate limits"
}
# Main troubleshooting function
main() {
troubleshoot_prowler
echo ""
fix_common_errors
echo ""
diagnose_performance
}
# Run troubleshooting
main
Resources and Documentation
Official Resources
- Prowler GitHub Repository - Source code and documentation
- Prowler Documentation - Official documentation and guides
- Prowler Checks Database - Available security checks
- Prowler Community - Community discussions and support
Compliance Resources
- CIS AWS Foundations Benchmark - CIS benchmark documentation
- AWS Security Best Practices - AWS security guidelines
- NIST Cybersecurity Framework - NIST framework documentation
- PCI DSS Requirements - PCI DSS compliance documentation
Integration Examples
- Prowler CI/CD Examples - Official integration examples
- AWS Security Hub Integration - Security Hub integration
- Terraform Prowler Module - Terraform integration
- CloudFormation Templates - CloudFormation deployment templates