chore: script to generate AWS accounts list from AWS Org for bulk provisioning (#8903)

This commit is contained in:
Andoni Alonso
2025-10-22 22:23:14 +02:00
committed by GitHub
parent 4b160257b9
commit 8d4fa46038
9 changed files with 1029 additions and 65 deletions

View File

@@ -114,7 +114,8 @@
"group": "Tutorials",
"pages": [
"user-guide/tutorials/prowler-app-sso-entra",
"user-guide/tutorials/bulk-provider-provisioning"
"user-guide/tutorials/bulk-provider-provisioning",
"user-guide/tutorials/aws-organizations-bulk-provisioning"
]
}
]

View File

@@ -0,0 +1,491 @@
---
title: 'AWS Organizations Bulk Provisioning in Prowler'
---
Prowler offers an automated tool to discover and provision all AWS accounts within an AWS Organization. This streamlines onboarding for organizations managing multiple AWS accounts by automatically generating the configuration needed for bulk provisioning.
The tool, `aws_org_generator.py`, complements the [Bulk Provider Provisioning](./bulk-provider-provisioning) tool and is available in the Prowler repository at: [util/prowler-bulk-provisioning](https://github.com/prowler-cloud/prowler/tree/master/util/prowler-bulk-provisioning)
<Note>
Native support for bulk provisioning AWS Organizations and similar multi-account structures directly in the Prowler UI/API is on the official roadmap.
Track progress and vote for this feature at: [Bulk Provisioning in the UI/API for AWS Organizations](https://roadmap.prowler.com/p/builk-provisioning-in-the-uiapi-for-aws-organizations-and-alike)
</Note>
{/* TODO: Add screenshot of the tool in action */}
## Overview
The AWS Organizations Bulk Provisioning tool simplifies multi-account onboarding by:
* Automatically discovering all active accounts in an AWS Organization
* Generating YAML configuration files for bulk provisioning
* Supporting account filtering and custom role configurations
* Eliminating manual entry of account IDs and role ARNs
## Prerequisites
### Requirements
* Python 3.7 or higher
* AWS credentials with Organizations read access
* ProwlerRole (or custom role) deployed across all target accounts
* Prowler API key (from Prowler Cloud or self-hosted Prowler App)
* For self-hosted Prowler App, remember to [point to your API base URL](./bulk-provider-provisioning#custom-api-endpoints)
* Learn how to create API keys: [Prowler App API Keys](../providers/prowler-app-api-keys)
### Deploying ProwlerRole Across AWS Organizations
Before using the AWS Organizations generator, deploy the ProwlerRole across all accounts in the organization using CloudFormation StackSets.
<Note>
**Follow the official documentation:**
[Deploying Prowler IAM Roles Across AWS Organizations](../providers/aws/organizations#deploying-prowler-iam-roles-across-aws-organizations)
**Key points:**
* Use CloudFormation StackSets from the management account
* Deploy to all organizational units (OUs) or specific OUs
* Use an external ID for enhanced security
* Ensure the role has necessary permissions for Prowler scans
</Note>
### Installation
Clone the repository and install required dependencies:
```bash
git clone https://github.com/prowler-cloud/prowler.git
cd prowler/util/prowler-bulk-provisioning
pip install -r requirements-aws-org.txt
```
### AWS Credentials Setup
Configure AWS credentials with Organizations read access:
* **Management account credentials**, or
* **Delegated administrator account** with `organizations:ListAccounts` permission
Required IAM permissions:
```json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"organizations:ListAccounts",
"organizations:DescribeOrganization"
],
"Resource": "*"
}
]
}
```
### Prowler API Key Setup
Configure your Prowler API key:
```bash
export PROWLER_API_KEY="pk_example-api-key"
```
To create an API key:
1. Log in to Prowler Cloud or Prowler App
2. Click **Profile** → **Account**
3. Click **Create API Key**
4. Provide a descriptive name and optionally set an expiration date
5. Copy the generated API key (it will only be shown once)
For detailed instructions, see: [Prowler App API Keys](../providers/prowler-app-api-keys)
## Basic Usage
### Generate Configuration for All Accounts
To generate a YAML configuration file for all active accounts in the organization:
```bash
python aws_org_generator.py -o aws-accounts.yaml --external-id prowler-ext-id-2024
```
This command:
1. Lists all ACTIVE accounts in the organization
2. Generates YAML entries for each account
3. Saves the configuration to `aws-accounts.yaml`
**Output:**
```
Fetching accounts from AWS Organizations...
Found 47 active accounts in organization
Generated configuration for 47 accounts
Configuration written to: aws-accounts.yaml
Next steps:
1. Review the generated file: cat aws-accounts.yaml | head -n 20
2. Run bulk provisioning: python prowler_bulk_provisioning.py aws-accounts.yaml
```
### Review Generated Configuration
Review the generated YAML configuration:
```bash
head -n 20 aws-accounts.yaml
```
**Example output:**
```yaml
- provider: aws
uid: '111111111111'
alias: Production-Account
auth_method: role
credentials:
role_arn: arn:aws:iam::111111111111:role/ProwlerRole
external_id: prowler-ext-id-2024
- provider: aws
uid: '222222222222'
alias: Development-Account
auth_method: role
credentials:
role_arn: arn:aws:iam::222222222222:role/ProwlerRole
external_id: prowler-ext-id-2024
```
### Dry Run Mode
Test the configuration without writing a file:
```bash
python aws_org_generator.py \
--external-id prowler-ext-id-2024 \
--dry-run
```
## Advanced Configuration
### Using a Specific AWS Profile
Specify an AWS profile when multiple profiles are configured:
```bash
python aws_org_generator.py \
-o aws-accounts.yaml \
--profile org-management-admin \
--external-id prowler-ext-id-2024
```
### Excluding Specific Accounts
Exclude the management account or other accounts from provisioning:
```bash
python aws_org_generator.py \
-o aws-accounts.yaml \
--external-id prowler-ext-id-2024 \
--exclude 123456789012,210987654321
```
Common exclusion scenarios:
* Management account (requires different permissions)
* Break-glass accounts (emergency access)
* Suspended or archived accounts
### Including Only Specific Accounts
Generate configuration for specific accounts only:
```bash
python aws_org_generator.py \
-o aws-accounts.yaml \
--external-id prowler-ext-id-2024 \
--include 111111111111,222222222222,333333333333
```
### Custom Role Name
Specify a custom role name if not using the default `ProwlerRole`:
```bash
python aws_org_generator.py \
-o aws-accounts.yaml \
--role-name ProwlerExecutionRole \
--external-id prowler-ext-id-2024
```
### Custom Alias Format
Customize account aliases using template variables:
```bash
# Use account name and ID
python aws_org_generator.py \
-o aws-accounts.yaml \
--alias-format "{name}-{id}" \
--external-id prowler-ext-id-2024
# Use email prefix
python aws_org_generator.py \
-o aws-accounts.yaml \
--alias-format "{email}" \
--external-id prowler-ext-id-2024
```
Available template variables:
* `{name}` - Account name
* `{id}` - Account ID
* `{email}` - Account email
### Additional Role Assumption Options
Configure optional role assumption parameters:
```bash
python aws_org_generator.py \
-o aws-accounts.yaml \
--role-name ProwlerRole \
--external-id prowler-ext-id-2024 \
--session-name prowler-scan-session \
--duration-seconds 3600
```
## Complete Workflow Example
<Steps>
<Step title="Deploy ProwlerRole Using StackSets">
1. Log in to the AWS management account
2. Open CloudFormation → StackSets
3. Create a new StackSet using the [Prowler role template](https://github.com/prowler-cloud/prowler/blob/master/permissions/templates/cloudformation/prowler-scan-role.yml)
4. Deploy to all organizational units
5. Use a unique external ID (e.g., `prowler-org-2024-abc123`)
{/* TODO: Add screenshot of CloudFormation StackSets deployment */}
</Step>
<Step title="Generate YAML Configuration">
Configure AWS credentials and generate the YAML file:
```bash
# Using management account credentials
export AWS_PROFILE=org-management
# Generate configuration
python aws_org_generator.py \
-o aws-org-accounts.yaml \
--external-id prowler-org-2024-abc123 \
--exclude 123456789012
```
**Output:**
```
Fetching accounts from AWS Organizations...
Using AWS profile: org-management
Found 47 active accounts in organization
Generated configuration for 46 accounts
Configuration written to: aws-org-accounts.yaml
Next steps:
1. Review the generated file: cat aws-org-accounts.yaml | head -n 20
2. Run bulk provisioning: python prowler_bulk_provisioning.py aws-org-accounts.yaml
```
</Step>
<Step title="Review Generated Configuration">
Verify the generated YAML configuration:
```bash
# View first 20 lines
head -n 20 aws-org-accounts.yaml
# Check for unexpected accounts
grep "uid:" aws-org-accounts.yaml
# Verify role ARNs
grep "role_arn:" aws-org-accounts.yaml | head -5
# Count accounts
grep "provider: aws" aws-org-accounts.yaml | wc -l
```
</Step>
<Step title="Run Bulk Provisioning">
Provision all accounts to Prowler Cloud or Prowler App:
```bash
# Set Prowler API key
export PROWLER_API_KEY="pk_example-api-key"
# Run bulk provisioning with connection testing
python prowler_bulk_provisioning.py aws-org-accounts.yaml
```
**With custom options:**
```bash
python prowler_bulk_provisioning.py aws-org-accounts.yaml \
--concurrency 10 \
--timeout 120
```
**Successful output:**
```
[1] ✅ Created provider (id=db9a8985-f9ec-4dd8-b5a0-e05ab3880bed)
[1] ✅ Created secret (id=466f76c6-5878-4602-a4bc-13f9522c1fd2)
[1] ✅ Connection test: Connected
[2] ✅ Created provider (id=7a99f789-0cf5-4329-8279-2d443a962676)
[2] ✅ Created secret (id=c5702180-f7c4-40fd-be0e-f6433479b126)
[2] ✅ Connection test: Connected
Done. Success: 47 Failures: 0
```
{/* TODO: Add screenshot of successful bulk provisioning output */}
</Step>
</Steps>
## Command Reference
### Full Command-Line Options
```bash
python aws_org_generator.py \
-o OUTPUT_FILE \
--role-name ROLE_NAME \
--external-id EXTERNAL_ID \
--session-name SESSION_NAME \
--duration-seconds SECONDS \
--alias-format FORMAT \
--exclude ACCOUNT_IDS \
--include ACCOUNT_IDS \
--profile AWS_PROFILE \
--region AWS_REGION \
--dry-run
```
## Troubleshooting
### Error: "No AWS credentials found"
**Solution:** Configure AWS credentials using one of these methods:
```bash
# Method 1: AWS CLI configure
aws configure
# Method 2: Environment variables
export AWS_ACCESS_KEY_ID=your-key-id
export AWS_SECRET_ACCESS_KEY=your-secret-key
# Method 3: Use AWS profile
export AWS_PROFILE=org-management
```
### Error: "Access denied to AWS Organizations API"
**Cause:** Current credentials don't have permission to list organization accounts.
**Solution:**
* Ensure management account credentials are used
* Verify IAM permissions include `organizations:ListAccounts`
* Check IAM policies for Organizations access
### Error: "AWS Organizations is not enabled"
**Cause:** The account is not part of an organization.
**Solution:** This tool requires an AWS Organization. Create one in the AWS Organizations console or use standard bulk provisioning for standalone accounts.
### No Accounts Generated After Filters
**Cause:** All accounts were filtered out by `--exclude` or `--include` options.
**Solution:** Review filter options and verify account IDs are correct:
```bash
# List all accounts in organization
aws organizations list-accounts --query "Accounts[?Status=='ACTIVE'].[Id,Name]" --output table
```
### Connection Test Failures During Bulk Provisioning
**Cause:** ProwlerRole may not be deployed correctly or credentials are invalid.
**Solution:**
* Verify StackSet deployment status in CloudFormation
* Check role trust policy includes correct external ID
* Test role assumption manually:
```bash
aws sts assume-role \
--role-arn arn:aws:iam::123456789012:role/ProwlerRole \
--role-session-name test \
--external-id prowler-ext-id-2024
```
## Security Best Practices
### Use External ID
Always use an external ID when assuming cross-account roles:
```bash
python aws_org_generator.py \
-o aws-accounts.yaml \
--external-id $(uuidgen | tr '[:upper:]' '[:lower:]')
```
The external ID must match the one configured in the ProwlerRole trust policy across all accounts.
### Exclude Sensitive Accounts
Exclude accounts that shouldn't be scanned or require special handling:
```bash
python aws_org_generator.py \
-o aws-accounts.yaml \
--external-id prowler-ext-id \
--exclude 123456789012,111111111111 # management, break-glass accounts
```
### Review Generated Configuration
Always review the generated YAML before provisioning:
```bash
# Check for unexpected accounts
grep "uid:" aws-org-accounts.yaml
# Verify role ARNs
grep "role_arn:" aws-org-accounts.yaml | head -5
# Count accounts
grep "provider: aws" aws-org-accounts.yaml | wc -l
```
## Next Steps
<Columns cols={2}>
<Card title="Bulk Provider Provisioning" icon="terminal" href="/user-guide/tutorials/bulk-provider-provisioning">
Learn how to bulk provision providers in Prowler.
</Card>
<Card title="Prowler App" icon="pen-to-square" href="/user-guide/tutorials/prowler-app">
Detailed instructions on how to use Prowler.
</Card>
</Columns>

View File

@@ -17,14 +17,18 @@ The Bulk Provider Provisioning tool automates the creation of cloud providers in
* Testing connections to verify successful authentication
* Processing multiple providers concurrently for efficiency
<Tip>
**Using AWS Organizations?** For organizations with many AWS accounts, use the automated [AWS Organizations Bulk Provisioning](./aws-organizations-bulk-provisioning) tool to automatically discover and generate configuration for all accounts in your organization.
</Tip>
## Prerequisites
### Requirements
* Python 3.7 or higher
* Prowler API token (from Prowler Cloud or self-hosted Prowler App)
* Prowler API key (from Prowler Cloud or self-hosted Prowler App)
* For self-hosted Prowler App, remember to [point to your API base URL](#custom-api-endpoints)
* Learn how to create API keys: [Prowler App API Keys](../providers/prowler-app-api-keys)
* Authentication credentials for target cloud providers
### Installation
@@ -39,28 +43,21 @@ pip install -r requirements.txt
### Authentication Setup
Configure your Prowler API token:
Configure your Prowler API key:
```bash
export PROWLER_API_TOKEN="your-prowler-api-token"
export PROWLER_API_KEY="pk_example-api-key"
```
To obtain an API token programmatically:
To create an API key:
```bash
export PROWLER_API_TOKEN=$(curl --location 'https://api.prowler.com/api/v1/tokens' \
--header 'Content-Type: application/vnd.api+json' \
--header 'Accept: application/vnd.api+json' \
--data-raw '{
"data": {
"type": "tokens",
"attributes": {
"email": "your@email.com",
"password": "your-password"
}
}
}' | jq -r .data.attributes.access)
```
1. Log in to Prowler Cloud or Prowler App
2. Click **Profile** → **Account**
3. Click **Create API Key**
4. Provide a descriptive name and optionally set an expiration date
5. Copy the generated API key (it will only be shown once)
For detailed instructions, see: [Prowler App API Keys](../providers/prowler-app-api-keys)
## Configuration File Structure
@@ -340,11 +337,11 @@ Done. Success: 2 Failures: 0
## Troubleshooting
### Invalid API Token
### Invalid API Key
```
Error: 401 Unauthorized
Solution: Verify your PROWLER_API_TOKEN or --token parameter
Solution: Verify your PROWLER_API_KEY environment variable or --api-key parameter
```
### Network Timeouts

View File

@@ -19,6 +19,7 @@ A Python script to bulk-provision cloud providers in Prowler Cloud/App via REST
- **Flexible Authentication:** Supports various authentication methods per provider
- **Error Handling:** Comprehensive error reporting and validation
- **Connection Testing:** Built-in provider connection verification
- **AWS Organizations Support:** Automated YAML generation for all accounts in an AWS Organization
## How It Works
@@ -48,32 +49,105 @@ This two-step approach follows the Prowler API design where providers and their
pip install -r requirements.txt
```
3. Get your Prowler API token:
- **Prowler Cloud:** Generate token at https://api.prowler.com
- **Self-hosted Prowler App:** Generate token in your local instance
3. Get your Prowler API key:
- **Prowler Cloud:** Create an API key at https://api.prowler.com
- **Self-hosted Prowler App:** Create an API key in your local instance
- Click **Profile** → **Account** → **Create API Key**
```bash
export PROWLER_API_TOKEN=$(curl --location 'https://api.prowler.com/api/v1/tokens' \
--header 'Content-Type: application/vnd.api+json' \
--header 'Accept: application/vnd.api+json' \
--data-raw '{
"data": {
"type": "tokens",
"attributes": {
"email": "your@email.com",
"password": "your-password"
}
}
}' | jq -r .data.attributes.access)
export PROWLER_API_KEY="pk_example-api-key"
```
For detailed instructions on creating API keys, see: https://docs.prowler.com/user-guide/providers/prowler-app-api-keys
## AWS Organizations Integration
For organizations with many AWS accounts, use the included `aws_org_generator.py` script to automatically generate configuration for all accounts in your AWS Organization.
**📖 Full Guide:** See [AWS Organizations Bulk Provisioning Tutorial](https://docs.prowler.com/user-guide/tutorials/aws-organizations-bulk-provisioning) for complete documentation, examples, and troubleshooting.
### Prerequisites
Before using the AWS Organizations generator, deploy the ProwlerRole across all accounts using CloudFormation StackSets:
**Documentation:** [Deploying Prowler IAM Roles Across AWS Organizations](https://docs.prowler.com/projects/prowler-open-source/en/latest/tutorials/aws/organizations/#deploying-prowler-iam-roles-across-aws-organizations)
### Quick Start
1. Install additional dependencies:
```bash
pip install -r requirements-aws-org.txt
```
2. Generate YAML configuration for all organization accounts:
```bash
python aws_org_generator.py -o aws-accounts.yaml --external-id example-external-id
```
3. Run bulk provisioning:
```bash
python prowler_bulk_provisioning.py aws-accounts.yaml
```
### AWS Organizations Generator Options
```bash
python aws_org_generator.py -o aws-accounts.yaml \
--role-name ProwlerRole \
--external-id my-external-id \
--exclude 123456789012 \
--profile org-management
```
| Option | Description | Default |
|--------|-------------|---------|
| `-o, --output` | Output YAML file path | `aws-org-accounts.yaml` |
| `--role-name` | IAM role name across accounts | `ProwlerRole` |
| `--external-id` | External ID for role assumption | None (recommended) |
| `--session-name` | Session name for role assumption | None |
| `--duration-seconds` | Session duration in seconds | None |
| `--alias-format` | Alias template: `{name}`, `{id}`, `{email}` | `{name}` |
| `--exclude` | Comma-separated account IDs to exclude | None |
| `--include` | Comma-separated account IDs to include | None |
| `--profile` | AWS CLI profile name | Default credentials |
| `--region` | AWS region | `us-east-1` |
| `--dry-run` | Print to stdout without writing | `False` |
### Examples
**Generate config for all accounts with custom external ID:**
```bash
python aws_org_generator.py -o aws-accounts.yaml --external-id prowler-2024-abc123
```
**Exclude management account:**
```bash
python aws_org_generator.py -o aws-accounts.yaml \
--external-id prowler-ext-id \
--exclude 123456789012
```
**Use specific AWS profile:**
```bash
python aws_org_generator.py -o aws-accounts.yaml \
--profile org-admin \
--external-id prowler-ext-id
```
**Custom alias format:**
```bash
python aws_org_generator.py -o aws-accounts.yaml \
--alias-format "{name}-{id}" \
--external-id prowler-ext-id
```
## Configuration
### Environment Variables
```bash
export PROWLER_API_TOKEN="your-prowler-token"
export PROWLER_API_KEY="pk_example-api-key"
export PROWLER_API_BASE="https://api.prowler.com/api/v1" # Optional, defaults to Prowler Cloud
```
@@ -168,7 +242,7 @@ python prowler_bulk_provisioning.py providers.yaml \
|--------|-------------|---------|
| `input_file` | YAML file with provider entries | Required |
| `--base-url` | API base URL | `https://api.prowler.com/api/v1` |
| `--token` | Bearer token | `PROWLER_API_TOKEN` env var |
| `--api-key` | Prowler API key | `PROWLER_API_KEY` env var |
| `--providers-endpoint` | Providers API endpoint | `/providers` |
| `--concurrency` | Number of concurrent requests | `5` |
| `--timeout` | Per-request timeout in seconds | `60` |
@@ -241,8 +315,8 @@ The Prowler API supports the following authentication methods for GCP:
# OR inline:
# inline_json:
# type: "service_account"
# project_id: "your-project"
# private_key_id: "key-id"
# project_id: "example-project"
# private_key_id: "example-key-id"
# private_key: "-----BEGIN PRIVATE KEY-----\n..."
# client_email: "service-account@project.iam.gserviceaccount.com"
# client_id: "1234567890"
@@ -379,10 +453,10 @@ python prowler_bulk_provisioning.py providers.yaml --dry-run
### Common Issues
1. **Invalid API Token**
1. **Invalid API Key**
```
Error: 401 Unauthorized
Solution: Check your PROWLER_API_TOKEN or --token parameter
Solution: Check your PROWLER_API_KEY environment variable or --api-key parameter
```
2. **Network Timeouts**

View File

@@ -0,0 +1,333 @@
#!/usr/bin/env python3
"""
AWS Organizations Account Generator for Prowler Bulk Provisioning
Generates YAML configuration for all accounts in an AWS Organization,
ready to be used with prowler_bulk_provisioning.py.
Prerequisites:
- ProwlerRole (or custom role) must be deployed across all accounts
- AWS credentials with Organizations read access (typically management account)
- See: https://docs.prowler.com/projects/prowler-open-source/en/latest/tutorials/aws/organizations/#deploying-prowler-iam-roles-across-aws-organizations
"""
from __future__ import annotations
import argparse
import sys
from typing import Any, Dict, List, Optional
try:
import boto3
from botocore.exceptions import ClientError, NoCredentialsError
except ImportError:
sys.exit(
"boto3 is required. Install with: pip install boto3\n"
"Or install all dependencies: pip install -r requirements-aws-org.txt"
)
try:
import yaml
except ImportError:
sys.exit("PyYAML is required. Install with: pip install pyyaml")
def get_org_accounts(
profile: Optional[str] = None, region: Optional[str] = None
) -> List[Dict[str, Any]]:
"""
Retrieve all accounts from AWS Organizations.
Args:
profile: AWS CLI profile name
region: AWS region (defaults to us-east-1 for Organizations)
Returns:
List of account dictionaries with id, name, email, and status
"""
try:
session = boto3.Session(profile_name=profile, region_name=region or "us-east-1")
client = session.client("organizations")
accounts = []
paginator = client.get_paginator("list_accounts")
for page in paginator.paginate():
for account in page["Accounts"]:
# Only include ACTIVE accounts
if account["Status"] == "ACTIVE":
accounts.append(
{
"id": account["Id"],
"name": account["Name"],
"email": account["Email"],
"status": account["Status"],
}
)
return accounts
except NoCredentialsError:
sys.exit(
"No AWS credentials found. Configure credentials using:\n"
" - AWS CLI: aws configure\n"
" - Environment variables: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY\n"
" - IAM role if running on EC2/ECS/Lambda"
)
except ClientError as e:
error_code = e.response["Error"]["Code"]
if error_code == "AccessDeniedException":
sys.exit(
"Access denied to AWS Organizations API.\n"
"Ensure you are using credentials from the management account\n"
"with permissions to call organizations:ListAccounts"
)
elif error_code == "AWSOrganizationsNotInUseException":
sys.exit(
"AWS Organizations is not enabled for this account.\n"
"This script requires an AWS Organization to be set up."
)
else:
sys.exit(f"AWS API error: {e}")
except Exception as e:
sys.exit(f"Unexpected error listing accounts: {e}")
def generate_yaml_config(
accounts: List[Dict[str, Any]],
role_name: str = "ProwlerRole",
external_id: Optional[str] = None,
session_name: Optional[str] = None,
duration_seconds: Optional[int] = None,
alias_format: str = "{name}",
exclude_accounts: Optional[List[str]] = None,
include_accounts: Optional[List[str]] = None,
) -> List[Dict[str, Any]]:
"""
Generate YAML configuration for Prowler bulk provisioning.
Args:
accounts: List of account dictionaries from get_org_accounts
role_name: IAM role name (default: ProwlerRole)
external_id: External ID for role assumption (optional but recommended)
session_name: Session name for role assumption (optional)
duration_seconds: Session duration in seconds (optional)
alias_format: Format string for alias (supports {name}, {id}, {email})
exclude_accounts: List of account IDs to exclude
include_accounts: List of account IDs to include (if set, only these are included)
Returns:
List of provider configurations ready for YAML export
"""
exclude_accounts = exclude_accounts or []
include_accounts = include_accounts or []
providers = []
for account in accounts:
account_id = account["id"]
# Apply filters
if include_accounts and account_id not in include_accounts:
continue
if account_id in exclude_accounts:
continue
# Format alias using template
alias = alias_format.format(
name=account["name"], id=account_id, email=account["email"]
)
# Build role ARN
role_arn = f"arn:aws:iam::{account_id}:role/{role_name}"
# Build credentials section
credentials: Dict[str, Any] = {"role_arn": role_arn}
if external_id:
credentials["external_id"] = external_id
if session_name:
credentials["session_name"] = session_name
if duration_seconds:
credentials["duration_seconds"] = duration_seconds
# Build provider entry
provider = {
"provider": "aws",
"uid": account_id,
"alias": alias,
"auth_method": "role",
"credentials": credentials,
}
providers.append(provider)
return providers
def main():
"""Main function to generate AWS Organizations YAML configuration."""
parser = argparse.ArgumentParser(
description="Generate Prowler bulk provisioning YAML from AWS Organizations",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
# Basic usage - generate YAML for all accounts
python aws_org_generator.py -o aws-accounts.yaml
# Use custom role name and external ID
python aws_org_generator.py -o aws-accounts.yaml \\
--role-name ProwlerExecutionRole \\
--external-id my-external-id-12345
# Use specific AWS profile
python aws_org_generator.py -o aws-accounts.yaml \\
--profile org-management
# Exclude specific accounts (e.g., management account)
python aws_org_generator.py -o aws-accounts.yaml \\
--exclude 123456789012,210987654321
# Include only specific accounts
python aws_org_generator.py -o aws-accounts.yaml \\
--include 111111111111,222222222222
# Custom alias format
python aws_org_generator.py -o aws-accounts.yaml \\
--alias-format "{name}-{id}"
Prerequisites:
1. Deploy ProwlerRole across all accounts using CloudFormation StackSets:
https://docs.prowler.com/projects/prowler-open-source/en/latest/tutorials/aws/organizations/#deploying-prowler-iam-roles-across-aws-organizations
2. Ensure AWS credentials have Organizations read access:
- organizations:ListAccounts
- organizations:DescribeOrganization (optional)
""",
)
parser.add_argument(
"-o",
"--output",
default="aws-org-accounts.yaml",
help="Output YAML file path (default: aws-org-accounts.yaml)",
)
parser.add_argument(
"--role-name",
default="ProwlerRole",
help="IAM role name deployed across accounts (default: ProwlerRole)",
)
parser.add_argument(
"--external-id",
help="External ID for role assumption (recommended for security)",
)
parser.add_argument(
"--session-name", help="Session name for role assumption (optional)"
)
parser.add_argument(
"--duration-seconds",
type=int,
help="Session duration in seconds (optional, default: 3600)",
)
parser.add_argument(
"--alias-format",
default="{name}",
help="Alias format template. Available: {name}, {id}, {email} (default: {name})",
)
parser.add_argument(
"--exclude",
help="Comma-separated list of account IDs to exclude",
)
parser.add_argument(
"--include",
help="Comma-separated list of account IDs to include (if set, only these are processed)",
)
parser.add_argument(
"--profile",
help="AWS CLI profile name (uses default credentials if not specified)",
)
parser.add_argument(
"--region",
help="AWS region (default: us-east-1, Organizations is global but needs a region)",
)
parser.add_argument(
"--dry-run",
action="store_true",
help="Print configuration to stdout without writing file",
)
args = parser.parse_args()
# Parse exclude/include lists
exclude_accounts = (
[acc.strip() for acc in args.exclude.split(",")] if args.exclude else []
)
include_accounts = (
[acc.strip() for acc in args.include.split(",")] if args.include else []
)
print("Fetching accounts from AWS Organizations...")
if args.profile:
print(f"Using AWS profile: {args.profile}")
# Get accounts from Organizations
accounts = get_org_accounts(profile=args.profile, region=args.region)
if not accounts:
print("No active accounts found in organization.")
return
print(f"Found {len(accounts)} active accounts in organization")
# Generate YAML configuration
providers = generate_yaml_config(
accounts=accounts,
role_name=args.role_name,
external_id=args.external_id,
session_name=args.session_name,
duration_seconds=args.duration_seconds,
alias_format=args.alias_format,
exclude_accounts=exclude_accounts,
include_accounts=include_accounts,
)
if not providers:
print("No providers generated after applying filters.")
return
print(f"Generated configuration for {len(providers)} accounts")
# Output YAML
yaml_content = yaml.dump(
providers, default_flow_style=False, sort_keys=False, allow_unicode=True
)
if args.dry_run:
print("\n--- Generated YAML Configuration ---\n")
print(yaml_content)
else:
with open(args.output, "w", encoding="utf-8") as f:
f.write(yaml_content)
print(f"\nConfiguration written to: {args.output}")
print("\nNext steps:")
print(f" 1. Review the generated file: cat {args.output} | head -n 20")
print(
f" 2. Run bulk provisioning: python prowler_bulk_provisioning.py {args.output}"
)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,49 @@
# Example AWS Organizations Output
#
# This is an example of what aws_org_generator.py produces when run against
# an AWS Organization. This file can be directly used with prowler_bulk_provisioning.py
#
# Generated with:
# python aws_org_generator.py -o aws-org-accounts.yaml \
# --role-name ProwlerRole \
# --external-id prowler-ext-id-12345
- provider: aws
uid: '111111111111'
alias: Production-Account
auth_method: role
credentials:
role_arn: arn:aws:iam::111111111111:role/ProwlerRole
external_id: prowler-ext-id-12345
- provider: aws
uid: '222222222222'
alias: Development-Account
auth_method: role
credentials:
role_arn: arn:aws:iam::222222222222:role/ProwlerRole
external_id: prowler-ext-id-12345
- provider: aws
uid: '333333333333'
alias: Staging-Account
auth_method: role
credentials:
role_arn: arn:aws:iam::333333333333:role/ProwlerRole
external_id: prowler-ext-id-12345
- provider: aws
uid: '444444444444'
alias: Security-Account
auth_method: role
credentials:
role_arn: arn:aws:iam::444444444444:role/ProwlerRole
external_id: prowler-ext-id-12345
- provider: aws
uid: '555555555555'
alias: Logging-Account
auth_method: role
credentials:
role_arn: arn:aws:iam::555555555555:role/ProwlerRole
external_id: prowler-ext-id-12345

View File

@@ -7,7 +7,7 @@ Use with extreme caution. There is no undo.
Environment:
PROWLER_API_BASE (default: https://api.prowler.com/api/v1)
PROWLER_API_TOKEN (required unless --token is provided)
PROWLER_API_KEY (required unless --api-key is provided)
Usage:
python nuke_providers.py --confirm
@@ -39,12 +39,14 @@ import requests
# ----------------------------- CLI / Utils --------------------------------- #
def env_or_arg(token_arg: Optional[str]) -> str:
"""Get API token from argument or environment variable."""
token = token_arg or os.getenv("PROWLER_API_TOKEN")
if not token:
sys.exit("Missing API token. Set --token or PROWLER_API_TOKEN.")
return token
def env_or_arg(api_key_arg: Optional[str]) -> str:
"""Get API key from argument or environment variable."""
api_key = api_key_arg or os.getenv("PROWLER_API_KEY")
if not api_key:
sys.exit(
"Missing API key. Set --api-key or PROWLER_API_KEY environment variable."
)
return api_key
def normalize_base_url(url: str) -> str:
@@ -63,14 +65,14 @@ class ApiClient:
"""HTTP client for Prowler API."""
base_url: str
token: str
api_key: str
verify_ssl: bool = True
timeout: int = 60
def _headers(self) -> Dict[str, str]:
"""Generate HTTP headers for API requests."""
return {
"Authorization": f"Bearer {self.token}",
"Authorization": f"Api-Key {self.api_key}",
"Content-Type": "application/vnd.api+json",
"Accept": "application/vnd.api+json",
}
@@ -266,7 +268,9 @@ def main():
help="API base URL (default: env PROWLER_API_BASE or Prowler Cloud SaaS)",
)
parser.add_argument(
"--token", default=None, help="Bearer token (default: PROWLER_API_TOKEN)"
"--api-key",
default=None,
help="Prowler API key (default: PROWLER_API_KEY env variable)",
)
parser.add_argument(
"--filter-provider",
@@ -307,12 +311,12 @@ def main():
args = parser.parse_args()
token = env_or_arg(args.token)
api_key = env_or_arg(args.api_key)
base_url = normalize_base_url(args.base_url)
client = ApiClient(
base_url=base_url,
token=token,
api_key=api_key,
verify_ssl=not args.insecure,
timeout=args.timeout,
)

View File

@@ -132,12 +132,14 @@ def load_items(path: Path) -> List[Dict[str, Any]]:
sys.exit(f"Unsupported input file type: {ext}")
def env_or_arg(token_arg: Optional[str]) -> str:
"""Get API token from argument or environment variable."""
token = token_arg or os.getenv("PROWLER_API_TOKEN")
if not token:
sys.exit("Missing API token. Set --token or PROWLER_API_TOKEN.")
return token
def env_or_arg(api_key_arg: Optional[str]) -> str:
"""Get API key from argument or environment variable."""
api_key = api_key_arg or os.getenv("PROWLER_API_KEY")
if not api_key:
sys.exit(
"Missing API key. Set --api-key or PROWLER_API_KEY environment variable."
)
return api_key
def normalize_base_url(url: str) -> str:
@@ -395,14 +397,14 @@ class ApiClient:
"""HTTP client for Prowler API."""
base_url: str
token: str
api_key: str
verify_ssl: bool = True
timeout: int = 60
def _headers(self) -> Dict[str, str]:
"""Generate HTTP headers for API requests."""
return {
"Authorization": f"Bearer {self.token}",
"Authorization": f"Api-Key {self.api_key}",
"Content-Type": "application/vnd.api+json",
"Accept": "application/vnd.api+json",
}
@@ -564,7 +566,9 @@ def main():
help="API base URL (default: env PROWLER_API_BASE or Prowler Cloud SaaS).",
)
parser.add_argument(
"--token", default=None, help="Bearer token (default: PROWLER_API_TOKEN)."
"--api-key",
default=None,
help="Prowler API key (default: PROWLER_API_KEY env variable).",
)
parser.add_argument(
"--providers-endpoint",
@@ -600,7 +604,7 @@ def main():
)
args = parser.parse_args()
token = env_or_arg(args.token)
api_key = env_or_arg(args.api_key)
base_url = normalize_base_url(args.base_url)
items = load_items(Path(args.input_file))
@@ -610,7 +614,7 @@ def main():
client = ApiClient(
base_url=base_url,
token=token,
api_key=api_key,
verify_ssl=not args.insecure,
timeout=args.timeout,
)

View File

@@ -0,0 +1,11 @@
# AWS Organizations Generator Dependencies
#
# This extends the base requirements.txt for the AWS Organizations generator
# Include base dependencies
-r requirements.txt
# AWS SDK for Python
boto3>=1.26.0
# Note: PyYAML is already included in requirements.txt