# Prowler Provider Bulk Provisioning

A Python script to bulk-provision cloud providers in Prowler Cloud/App via REST API. This tool streamlines the process of adding multiple cloud providers to Prowler by reading configuration from YAML and making API calls with concurrency and retry support.

## Supported Providers

- **AWS** (Amazon Web Services)
- **Azure** (Microsoft Azure)
- **GCP** (Google Cloud Platform)
- **Kubernetes**
- **M365** (Microsoft 365)
- **GitHub**

## Features

- **Concurrent Processing:** Configurable concurrency for faster bulk operations
- **Retry Logic:** Built-in retry mechanism for handling temporary API failures
- **Dry-Run Mode:** Test configuration without making actual API calls
- **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

The script uses a two-step process to provision providers in Prowler:

1. **Provider Creation:** Creates the provider with basic information (provider type, UID, alias)
2. **Secret Creation:** Creates and links authentication credentials as a separate secret resource

This two-step approach follows the Prowler API design where providers and their credentials are managed as separate but linked resources, providing better security and flexibility.

## Installation

### Requirements

- Python 3.7 or higher
- Required packages (install via requirements.txt)

### Setup

1. Clone or navigate to the Prowler repository:
   ```bash
   cd contrib/other-contrib/provider-bulk-importer
   ```

2. Install dependencies:
   ```bash
   pip install -r requirements.txt
   ```

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_KEY="pk_example-api-key"
  ```

  For detailed instructions on creating API keys, see: https://docs.prowler.com/user-guide/tutorials/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_KEY="pk_example-api-key"
export PROWLER_API_BASE="https://api.prowler.com/api/v1"  # Optional, defaults to Prowler Cloud
```

### Provider Configuration Files

Create a configuration file (YAML recommended) listing the providers to add:

#### YAML Format (Recommended)

```yaml
# providers.yaml
- provider: aws
  uid: "123456789012"              # AWS Account ID
  alias: "prod-root"
  auth_method: role                # role | credentials
  credentials:
    role_arn: "arn:aws:iam::123456789012:role/ProwlerScan"
    external_id: "ext-abc123"      # optional
    session_name: "prowler-bulk"   # optional
    duration_seconds: 3600         # optional

- provider: aws
  uid: "210987654321"
  alias: "dev"
  auth_method: credentials         # long/short-lived keys
  credentials:
    access_key_id: "AKIA..."
    secret_access_key: "..."
    session_token: "..."           # optional

- provider: azure
  uid: "00000000-1111-2222-3333-444444444444" # Subscription ID
  alias: "sub-eastus"
  auth_method: service_principal
  credentials:
    tenant_id: "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
    client_id: "ffffffff-1111-2222-3333-444444444444"
    client_secret: "..."

- provider: gcp
  uid: "my-gcp-project-id"         # Project ID
  alias: "gcp-prod"
  auth_method: service_account     # Service Account authentication
  credentials:
    service_account_key_json_path: "./gcp-key.json"

- provider: kubernetes
  uid: "my-eks-context"            # kubeconfig context name
  alias: "eks-prod"
  auth_method: kubeconfig
  credentials:
    kubeconfig_path: "~/.kube/config"

- provider: m365
  uid: "contoso.onmicrosoft.com"   # Domain ID
  alias: "contoso"
  auth_method: service_principal
  credentials:
    tenant_id: "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
    client_id: "ffffffff-1111-2222-3333-444444444444"
    client_secret: "..."

- provider: github
  uid: "my-org"                    # organization or username
  alias: "gh-org"
  auth_method: personal_access_token  # oauth_app_token | github_app
  credentials:
    token: "ghp_..."
```

## Usage

### Basic Usage

```bash
python prowler_bulk_provisioning.py providers.yaml
```

### Advanced Usage

```bash
python prowler_bulk_provisioning.py providers.yaml \
  --base-url https://api.prowler.com/api/v1 \
  --providers-endpoint /providers \
  --concurrency 6 \
  --timeout 120
```

### Command Line Options

| Option | Description | Default |
|--------|-------------|---------|
| `input_file` | YAML file with provider entries | Required |
| `--base-url` | API base URL | `https://api.prowler.com/api/v1` |
| `--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` |
| `--insecure` | Disable TLS verification | `False` |
| `--dry-run` | Print payloads without sending | `False` |
| `--test-provider` | Test connection after creating each provider (true/false) | `true` (enabled by default) |
| `--test-provider-only` | Only test connections for existing providers (skip creation) | `False` |

### Self-hosted Prowler App

For self-hosted installations:

```bash
python prowler_bulk_provisioning.py providers.yaml \
  --base-url http://localhost:8080/api/v1
```

## Provider-Specific Configuration

### AWS Authentication Methods

#### IAM Role (Recommended)
```yaml
- provider: aws
  uid: "123456789012"
  alias: "prod"
  auth_method: role
  credentials:
    role_arn: "arn:aws:iam::123456789012:role/ProwlerScan"
    external_id: "optional-external-id"
```

#### Access Keys
```yaml
- provider: aws
  uid: "123456789012"
  alias: "dev"
  auth_method: credentials
  credentials:
    access_key_id: "AKIA..."
    secret_access_key: "..."
    session_token: "..."  # optional for temporary credentials
```

### Azure Authentication

```yaml
- provider: azure
  uid: "subscription-uuid"
  alias: "azure-prod"
  auth_method: service_principal
  credentials:
    tenant_id: "tenant-uuid"
    client_id: "client-uuid"
    client_secret: "client-secret"
```

### GCP Authentication

The Prowler API supports the following authentication methods for GCP:

#### Method 1: Service Account JSON (Recommended)
```yaml
- provider: gcp
  uid: "project-id"
  alias: "gcp-prod"
  auth_method: service_account  # or 'service_account_json'
  credentials:
    service_account_key_json_path: "/path/to/key.json"
    # OR inline:
    # inline_json:
    #   type: "service_account"
    #   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"
    #   auth_uri: "https://accounts.google.com/o/oauth2/auth"
    #   token_uri: "https://oauth2.googleapis.com/token"
    #   auth_provider_x509_cert_url: "https://www.googleapis.com/oauth2/v1/certs"
    #   client_x509_cert_url: "https://www.googleapis.com/robot/v1/metadata/x509/..."
```

#### Method 2: OAuth2 Credentials
```yaml
- provider: gcp
  uid: "project-id"
  alias: "gcp-prod"
  auth_method: oauth2  # or 'adc' for Application Default Credentials
  credentials:
    client_id: "123456789012345678901.apps.googleusercontent.com"
    client_secret: "GOCSPX-xxxxxxxxxxxxxxxxx"
    refresh_token: "1//0exxxxxxxxxxxxxxxxx"
```

### Kubernetes Authentication

```yaml
- provider: kubernetes
  uid: "context-name"
  alias: "k8s-prod"
  auth_method: kubeconfig
  credentials:
    kubeconfig_path: "~/.kube/config"
    # OR
    # kubeconfig_inline: |
    #   apiVersion: v1
    #   clusters: ...
```

### Microsoft 365 Authentication

```yaml
- provider: m365
  uid: "domain.onmicrosoft.com"
  alias: "m365-tenant"
  auth_method: service_principal
  credentials:
    tenant_id: "tenant-uuid"
    client_id: "client-uuid"
    client_secret: "client-secret"
```

### GitHub Authentication

#### Personal Access Token
```yaml
- provider: github
  uid: "organization-name"
  alias: "gh-org"
  auth_method: personal_access_token
  credentials:
    token: "ghp_..."
```

#### GitHub App
```yaml
- provider: github
  uid: "organization-name"
  alias: "gh-org"
  auth_method: github_app
  credentials:
    app_id: "123456"
    private_key_path: "/path/to/private-key.pem"
    # OR
    # private_key_inline: "-----BEGIN RSA PRIVATE KEY-----\n..."
```

## Connection Testing

The script includes built-in connection testing to verify that providers can successfully authenticate with their respective cloud services.

By default, the script tests connections immediately after creating providers:

```bash
python prowler_bulk_provisioning.py providers.yaml
```

This will:
1. Create the provider
2. Add credentials
3. Test the connection
4. Report connection status

To skip connection testing, use:

```bash
python prowler_bulk_provisioning.py providers.yaml --test-provider false
```

### Test Existing Providers

Test connections for already existing providers without creating new ones:

```bash
python prowler_bulk_provisioning.py providers.yaml --test-provider-only
```

This is useful for:
- Verifying existing provider configurations
- Debugging authentication issues
- Regular connection health checks
- Testing after credential updates

### Example Output

```text
[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: Not connected
```

## Advanced Features

### Dry Run Mode

Test your configuration without making API calls:

```bash
python prowler_bulk_provisioning.py providers.yaml --dry-run
```

## Troubleshooting

### Common Issues

1. **Invalid API Key**
   ```text
   Error: 401 Unauthorized
   Solution: Check your PROWLER_API_KEY environment variable or --api-key parameter
   ```

2. **Network Timeouts**
   ```text
   Error: Request timeout
   Solution: Increase --timeout value or check network connectivity
   ```

3. **Invalid Provider Configuration**
   ```text
   Error: Each item must include 'provider' and 'uid'
   Solution: Verify all required fields are present in your config file
   ```

4. **File Not Found Errors**
   ```text
   Error: No such file or directory
   Solution: Check file paths for credentials files (JSON keys, kubeconfig, etc.)
   ```

## Examples

See the `examples/` directory for sample configuration files:

- `examples/simple-providers.yaml` - Basic example with minimal configuration

## Support

For issues and questions:

1. Check the [Prowler documentation](https://docs.prowler.com)
2. Review the [API documentation](https://api.prowler.com/api/v1/docs)
3. Open an issue in the [Prowler repository](https://github.com/prowler-cloud/prowler)

## License

This tool is part of the Prowler project and follows the same licensing terms.
