From 159b95cb2455688db114d4239d957619cfcf6b37 Mon Sep 17 00:00:00 2001 From: Sergio Date: Thu, 24 Oct 2024 10:33:28 -0700 Subject: [PATCH] feat(gcp): add --organzation-id flag --- docs/tutorials/gcp/organization.md | 9 +++++++ mkdocs.yml | 1 + prowler/providers/common/provider.py | 1 + prowler/providers/gcp/gcp_provider.py | 24 +++++++++++++++---- .../providers/gcp/lib/arguments/arguments.py | 8 +++++++ tests/lib/cli/parser_test.py | 8 +++++++ 6 files changed, 47 insertions(+), 4 deletions(-) create mode 100644 docs/tutorials/gcp/organization.md diff --git a/docs/tutorials/gcp/organization.md b/docs/tutorials/gcp/organization.md new file mode 100644 index 00000000000..60b4a7a4bc2 --- /dev/null +++ b/docs/tutorials/gcp/organization.md @@ -0,0 +1,9 @@ +# GCP Organization + +By default, Prowler will scan all the Google Cloud projects that the authenticated user has access to. + +If you want to scan only projects from a specific organization, you can use the `--organization-id` argument. + +```console +prowler gcp --organization-id organization-id +``` diff --git a/mkdocs.yml b/mkdocs.yml index d696191908e..bc9ce5604b3 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -87,6 +87,7 @@ nav: - Google Cloud: - Authentication: tutorials/gcp/authentication.md - Projects: tutorials/gcp/projects.md + - Organization: tutorials/gcp/organization.md - Kubernetes: - In-Cluster Execution: tutorials/kubernetes/in-cluster.md - Non In-Cluster Execution: tutorials/kubernetes/outside-cluster.md diff --git a/prowler/providers/common/provider.py b/prowler/providers/common/provider.py index 6ba8a1ea200..5e7f22ec291 100644 --- a/prowler/providers/common/provider.py +++ b/prowler/providers/common/provider.py @@ -202,6 +202,7 @@ def init_global_provider(arguments: Namespace) -> None: ) elif "gcp" in provider_class_name.lower(): provider_class( + arguments.organization_id, arguments.project_id, arguments.excluded_project_id, arguments.credentials_file, diff --git a/prowler/providers/gcp/gcp_provider.py b/prowler/providers/gcp/gcp_provider.py index 6d9a4d9d19d..6d4ed0e2084 100644 --- a/prowler/providers/gcp/gcp_provider.py +++ b/prowler/providers/gcp/gcp_provider.py @@ -47,6 +47,7 @@ class GcpProvider(Provider): def __init__( self, + organization_id: str = None, project_ids: list = None, excluded_project_ids: list = None, credentials_file: str = None, @@ -65,6 +66,7 @@ def __init__( GCP Provider constructor Args: + organization_id: str project_ids: list excluded_project_ids: list credentials_file: str @@ -95,7 +97,7 @@ def __init__( self._project_ids = [] self._projects = {} self._excluded_project_ids = [] - accessible_projects = self.get_projects(self._session) + accessible_projects = self.get_projects(self._session, organization_id) if not accessible_projects: logger.critical("No Project IDs can be accessed via Google Credentials.") raise GCPNoAccesibleProjectsError( @@ -428,15 +430,29 @@ def print_credentials(self): print_boxes(report_lines, report_title) @staticmethod - def get_projects(credentials) -> dict[str, GCPProject]: + def get_projects( + credentials: Credentials, organization_id: str + ) -> dict[str, GCPProject]: + """ + Get the projects accessible by the provided credentials. If an organization ID is provided, only the projects under that organization are returned. + Args: + credentials: Credentials + organization_id: str + Returns: + dict[str, GCPProject] + """ try: projects = {} service = discovery.build( "cloudresourcemanager", "v1", credentials=credentials ) - - request = service.projects().list() + if organization_id: + request = service.projects().list( + filter=f'parent.type:"organization" parent.id:"{organization_id}"' + ) + else: + request = service.projects().list() while request is not None: response = request.execute() diff --git a/prowler/providers/gcp/lib/arguments/arguments.py b/prowler/providers/gcp/lib/arguments/arguments.py index e9239cb3ab9..24b0958b83d 100644 --- a/prowler/providers/gcp/lib/arguments/arguments.py +++ b/prowler/providers/gcp/lib/arguments/arguments.py @@ -18,6 +18,14 @@ def init_parser(self): metavar="SERVICE_ACCOUNT", help="Impersonate a Google Service Account", ) + # Organizations + gcp_organization_subparser = gcp_parser.add_argument_group("Organization") + gcp_organization_subparser.add_argument( + "--organization-id", + nargs="?", + metavar="ORGANIZATION_ID", + help="GCP Organization ID to be scanned by Prowler", + ) # Projects gcp_projects_subparser = gcp_parser.add_argument_group("Projects") gcp_projects_subparser.add_argument( diff --git a/tests/lib/cli/parser_test.py b/tests/lib/cli/parser_test.py index f1d75b23745..b678c636c0b 100644 --- a/tests/lib/cli/parser_test.py +++ b/tests/lib/cli/parser_test.py @@ -1212,6 +1212,14 @@ def test_parser_gcp_auth_credentials_file(self): assert parsed.provider == "gcp" assert parsed.credentials_file == file + def test_parser_gcp_organization_id(self): + argument = "--organization-id" + organization = "test_organization" + command = [prowler_command, "gcp", argument, organization] + parsed = self.parser.parse(command) + assert parsed.provider == "gcp" + assert parsed.organization_id == organization + def test_parser_gcp_project_id(self): argument = "--project-id" project_1 = "test_project_1"