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—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 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. -
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:
CSV Classclass 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 theCSV
class:Transformdef 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["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 theCSV
class:Batch Write Data To Filedef 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:
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.