> ## Documentation Index
> Fetch the complete documentation index at: https://docs.prowler.com/llms.txt
> Use this file to discover all available pages before exploring further.

<AgentInstructions>

## Submitting Feedback

If you encounter incorrect, outdated, or confusing documentation on this page, submit feedback:

POST https://docs.prowler.com/feedback

```json
{
  "path": "/developer-guide/outputs",
  "feedback": "Description of the issue"
}
```

Only submit feedback when you have something specific and actionable to report.

</AgentInstructions>

# Create a Custom Output Format

## Introduction

Prowler supports multiple output formats, allowing users to tailor findings presentation to their needs. Custom output formats are valuable when integrating Prowler with third-party tools, generating specialized reports, or adapting data for specific workflows. By defining a custom output format, users can refine how findings are structured, extracting and displaying only the most relevant information.

* Output Organization in Prowler

  Prowler outputs are managed within the `/lib/outputs` directory. Each format—such as JSON, CSV, HTML, SARIF—is implemented as a Python class.

* Outputs are generated based on scan findings, which are stored as structured dictionaries containing details such as:

  * Resource IDs

  * Severities

  * Descriptions

  * Other relevant metadata

* Creation Guidelines

  Refer to the [Prowler Developer Guide](https://docs.prowler.com/projects/prowler-open-source/en/latest/) for insights into Prowler’s architecture and best practices for creating custom outputs.

* Identify the most suitable integration method for the output being targeted.

## Steps to Create a Custom Output Format

### Schema

* Output Class:

  * The class must inherit from `Output`. Review the [Output Class](https://github.com/prowler-cloud/prowler/blob/master/prowler/lib/outputs/output.py).

  * Create a class that encapsulates the required attributes and methods for interacting with the target platform. Below the code for the `CSV` class is presented:

  ```python title="CSV Class" theme={null}
  class CSV(Output):
      def transform(self, findings: List[Finding]) -> None:
          """Transforms the findings into the CSV format.

          Args:
              findings (list[Finding]): a list of Finding objects

          """
      ...
  ```

  * Transform Method:

    * This method will transform the findings provided by Prowler to a specific format.

    #### Method Implementation

    The following example demonstrates the `transform` method for the `CSV` class:

    ```python title="Transform" theme={null}
    def transform(self, findings: List[Finding]) -> None:
        """Transforms the findings into the CSV format.

        Args:
            findings (list[Finding]): a list of Finding objects

        """
        try:
            for finding in findings:
                finding_dict = {}
                finding_dict["AUTH_METHOD"] = finding.auth_method
                finding_dict["TIMESTAMP"] = finding.timestamp
                finding_dict["ACCOUNT_UID"] = finding.account_uid
                finding_dict["ACCOUNT_NAME"] = finding.account_name
                finding_dict["ACCOUNT_EMAIL"] = finding.account_email
                finding_dict["ACCOUNT_ORGANIZATION_UID"] = (
                    finding.account_organization_uid
                )
                finding_dict["ACCOUNT_ORGANIZATION_NAME"] = (
                    finding.account_organization_name
                )
                finding_dict["ACCOUNT_TAGS"] = unroll_dict(
                    finding.account_tags, separator=":"
                )
                finding_dict["FINDING_UID"] = finding.uid
                finding_dict["PROVIDER"] = finding.metadata.Provider
                finding_dict["CHECK_ID"] = finding.metadata.CheckID
                finding_dict["CHECK_TITLE"] = finding.metadata.CheckTitle
                finding_dict["CHECK_TYPE"] = unroll_list(finding.metadata.CheckType)
                finding_dict["STATUS"] = finding.status.value
                finding_dict["STATUS_EXTENDED"] = finding.status_extended
                finding_dict["MUTED"] = finding.muted
                finding_dict["SERVICE_NAME"] = finding.metadata.ServiceName
                finding_dict["SUBSERVICE_NAME"] = finding.metadata.SubServiceName
                finding_dict["SEVERITY"] = finding.metadata.Severity.value
                finding_dict["RESOURCE_TYPE"] = finding.metadata.ResourceType
                finding_dict["RESOURCE_UID"] = finding.resource_uid
                finding_dict["RESOURCE_NAME"] = finding.resource_name
                finding_dict["RESOURCE_DETAILS"] = finding.resource_details
                finding_dict["RESOURCE_TAGS"] = unroll_dict(finding.resource_tags)
                finding_dict["PARTITION"] = finding.partition
                finding_dict["REGION"] = finding.region
                finding_dict["DESCRIPTION"] = finding.metadata.Description
                finding_dict["RISK"] = finding.metadata.Risk
                finding_dict["RELATED_URL"] = finding.metadata.RelatedUrl
                finding_dict["ADDITIONAL_URLS"] = unroll_list(finding.metadata.AdditionalURLs)
                finding_dict["REMEDIATION_RECOMMENDATION_TEXT"] = (
                    finding.metadata.Remediation.Recommendation.Text
                )
                finding_dict["REMEDIATION_RECOMMENDATION_URL"] = (
                    finding.metadata.Remediation.Recommendation.Url
                )
                finding_dict["REMEDIATION_CODE_NATIVEIAC"] = (
                    finding.metadata.Remediation.Code.NativeIaC
                )
                finding_dict["REMEDIATION_CODE_TERRAFORM"] = (
                    finding.metadata.Remediation.Code.Terraform
                )
                finding_dict["REMEDIATION_CODE_CLI"] = (
                    finding.metadata.Remediation.Code.CLI
                )
                finding_dict["REMEDIATION_CODE_OTHER"] = (
                    finding.metadata.Remediation.Code.Other
                )
                finding_dict["COMPLIANCE"] = unroll_dict(
                    finding.compliance, separator=": "
                )
                finding_dict["CATEGORIES"] = unroll_list(finding.metadata.Categories)
                finding_dict["DEPENDS_ON"] = unroll_list(finding.metadata.DependsOn)
                finding_dict["RELATED_TO"] = unroll_list(finding.metadata.RelatedTo)
                finding_dict["NOTES"] = finding.metadata.Notes
                finding_dict["PROWLER_VERSION"] = finding.prowler_version
                self._data.append(finding_dict)
        except Exception as error:
            logger.error(
                f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
            )
    ```

  * Batch Write Data To File Method:

    * This method will write the modeled object to a file.

    #### Method Implementation

    The following example demonstrates the `batch_write_data_to_file` method for the `CSV` class:

    ```python title="Batch Write Data To File" theme={null}
    def batch_write_data_to_file(self) -> None:
        """Writes the findings to a file using the CSV format using the `Output._file_descriptor`."""
        try:
            if (
                getattr(self, "_file_descriptor", None)
                and not self._file_descriptor.closed
                and self._data
            ):
                csv_writer = DictWriter(
                    self._file_descriptor,
                    fieldnames=self._data[0].keys(),
                    delimiter=";",
                )
                csv_writer.writeheader()
                for finding in self._data:
                    csv_writer.writerow(finding)
                self._file_descriptor.close()
        except Exception as error:
            logger.error(
                f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
            )
    ```

### Integrating the Custom Output Format into Prowler

Once the custom output format is created, it must be integrated into Prowler to ensure compatibility with the existing architecture.

#### Reviewing Current Supported Outputs

Before implementing the new output format, examine the usage of currently supported formats to understand their structure and integration approach. Example: CSV Output Creation in Prowler

Below is an example of how Prowler generates and processes CSV output within its [codebase](https://github.com/prowler-cloud/prowler/blob/master/prowler/__main__.py):

```python title="CSV creation" theme={null}
if mode == "csv":
    csv_output = CSV(
        findings=finding_outputs,
        create_file_descriptor=True,
        file_path=f"{filename}{csv_file_suffix}",
    )
    generated_outputs["regular"].append(csv_output)
    # Write CSV Finding Object to file.
    csv_output.batch_write_data_to_file()
```

### Testing

* Verify that Prowler’s findings are accurately typed in the desired output format.

* Error Handling – Simulate edge cases to assess robustness and failure recovery mechanisms.

### Documentation

* Ensure the following elements are included:

  * Setup Instructions – List all necessary dependencies and installation steps.
  * Configuration details.
  * Example Use Cases – Provide practical scenarios demonstrating functionality.
  * Troubleshooting Guide – Document common issues and resolution steps.

* Comprehensive and clear documentation improves maintainability and simplifies onboarding of new users.
