diff --git a/prowler/CHANGELOG.md b/prowler/CHANGELOG.md index 1c41466b09..dbf0711422 100644 --- a/prowler/CHANGELOG.md +++ b/prowler/CHANGELOG.md @@ -7,6 +7,7 @@ All notable changes to the **Prowler SDK** are documented in this file. ### Added - Add Prowler ThreatScore for the Alibaba Cloud provider [(#9511)](https://github.com/prowler-cloud/prowler/pull/9511) - `compute_instance_group_multiple_zones` check for GCP provider [(#9566)](https://github.com/prowler-cloud/prowler/pull/9566) +- `compute_instance_group_autohealing_enabled` check for GCP provider [(#9690)](https://github.com/prowler-cloud/prowler/pull/9690) - Support AWS European Sovereign Cloud [(#9649)](https://github.com/prowler-cloud/prowler/pull/9649) - `compute_instance_disk_auto_delete_disabled` check for GCP provider [(#9604)](https://github.com/prowler-cloud/prowler/pull/9604) - Bedrock service pagination [(#9606)](https://github.com/prowler-cloud/prowler/pull/9606) diff --git a/prowler/providers/gcp/services/compute/compute_instance_group_autohealing_enabled/__init__.py b/prowler/providers/gcp/services/compute/compute_instance_group_autohealing_enabled/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/prowler/providers/gcp/services/compute/compute_instance_group_autohealing_enabled/compute_instance_group_autohealing_enabled.metadata.json b/prowler/providers/gcp/services/compute/compute_instance_group_autohealing_enabled/compute_instance_group_autohealing_enabled.metadata.json new file mode 100644 index 0000000000..4805252f1c --- /dev/null +++ b/prowler/providers/gcp/services/compute/compute_instance_group_autohealing_enabled/compute_instance_group_autohealing_enabled.metadata.json @@ -0,0 +1,37 @@ +{ + "Provider": "gcp", + "CheckID": "compute_instance_group_autohealing_enabled", + "CheckTitle": "Managed Instance Group has autohealing enabled with a valid health check", + "CheckType": [], + "ServiceName": "compute", + "SubServiceName": "", + "ResourceIdTemplate": "", + "Severity": "high", + "ResourceType": "compute.googleapis.com/InstanceGroupManager", + "ResourceGroup": "compute", + "Description": "Managed Instance Groups (MIGs) should have **autohealing** enabled with a valid health check configured. Autohealing automatically recreates unhealthy instances based on application-level health checks, ensuring continuous availability.", + "Risk": "Without autohealing, MIGs cannot detect application-level failures such as crashes, freezes, or memory issues. Instances that are technically running but experiencing problems will remain undetected and unreplaced, leading to:\n\n- **Service degradation** from unhealthy instances\n- **Extended downtime** during application failures\n- **Manual intervention** required to detect and replace failed instances", + "RelatedUrl": "", + "AdditionalURLs": [ + "https://cloud.google.com/compute/docs/instance-groups/autohealing-instances-in-migs", + "https://www.trendmicro.com/cloudoneconformity/knowledge-base/gcp/ComputeEngine/enable-instance-group-autohealing.html" + ], + "Remediation": { + "Code": { + "CLI": "gcloud compute instance-groups managed update INSTANCE_GROUP_NAME --health-check=HEALTH_CHECK_NAME --initial-delay=300 --zone=ZONE", + "NativeIaC": "", + "Other": "1. Navigate to Compute Engine > Instance groups\n2. Select the Managed Instance Group\n3. Click 'Edit'\n4. Under 'Autohealing', click 'Add health check'\n5. Select or create a health check\n6. Set an appropriate initial delay (e.g., 300 seconds)\n7. Click 'Save'", + "Terraform": "```hcl\nresource \"google_compute_instance_group_manager\" \"example\" {\n name = \"example-mig\"\n base_instance_name = \"example\"\n zone = \"us-central1-a\"\n target_size = 2\n\n version {\n instance_template = google_compute_instance_template.example.id\n }\n\n # Enable autohealing with health check\n auto_healing_policies {\n health_check = google_compute_health_check.example.id\n initial_delay_sec = 300\n }\n}\n\nresource \"google_compute_health_check\" \"example\" {\n name = \"example-health-check\"\n check_interval_sec = 10\n timeout_sec = 5\n\n http_health_check {\n port = 80\n }\n}\n```" + }, + "Recommendation": { + "Text": "Enable autohealing on all Managed Instance Groups by configuring a health check that validates application-level health. Set an appropriate initial delay to allow instances time to start before health checks begin.", + "Url": "https://hub.prowler.com/check/compute_instance_group_autohealing_enabled" + } + }, + "Categories": [ + "resilience" + ], + "DependsOn": [], + "RelatedTo": [], + "Notes": "" +} diff --git a/prowler/providers/gcp/services/compute/compute_instance_group_autohealing_enabled/compute_instance_group_autohealing_enabled.py b/prowler/providers/gcp/services/compute/compute_instance_group_autohealing_enabled/compute_instance_group_autohealing_enabled.py new file mode 100644 index 0000000000..3a362072ef --- /dev/null +++ b/prowler/providers/gcp/services/compute/compute_instance_group_autohealing_enabled/compute_instance_group_autohealing_enabled.py @@ -0,0 +1,50 @@ +from prowler.lib.check.models import Check, Check_Report_GCP +from prowler.providers.gcp.services.compute.compute_client import compute_client + + +class compute_instance_group_autohealing_enabled(Check): + """ + Ensure Managed Instance Groups have autohealing enabled with a valid health check. + + This check verifies whether GCP Managed Instance Groups (MIGs) have autohealing + policies configured with valid health check references. Autohealing automatically + recreates unhealthy instances based on application-level health checks. + + - PASS: The MIG has autohealing enabled with a valid health check configured. + - FAIL: The MIG has no autohealing policies or is missing a health check reference. + """ + + def execute(self) -> list[Check_Report_GCP]: + findings = [] + + for instance_group in compute_client.instance_groups: + report = Check_Report_GCP( + metadata=self.metadata(), + resource=instance_group, + location=instance_group.region, + ) + + if not instance_group.auto_healing_policies: + report.status = "FAIL" + report.status_extended = f"Managed Instance Group {instance_group.name} does not have autohealing enabled." + else: + has_valid_health_check = any( + policy.health_check + for policy in instance_group.auto_healing_policies + ) + + if has_valid_health_check: + health_checks = [ + policy.health_check + for policy in instance_group.auto_healing_policies + if policy.health_check + ] + report.status = "PASS" + report.status_extended = f"Managed Instance Group {instance_group.name} has autohealing enabled with health check(s): {', '.join(health_checks)}." + else: + report.status = "FAIL" + report.status_extended = f"Managed Instance Group {instance_group.name} has autohealing configured but is missing a valid health check reference." + + findings.append(report) + + return findings diff --git a/prowler/providers/gcp/services/compute/compute_service.py b/prowler/providers/gcp/services/compute/compute_service.py index f940726df7..283056acb4 100644 --- a/prowler/providers/gcp/services/compute/compute_service.py +++ b/prowler/providers/gcp/services/compute/compute_service.py @@ -382,7 +382,6 @@ class Compute(GCPService): ) def _get_regional_instance_groups(self, region: str) -> None: - """Fetch regional managed instance groups for all projects.""" for project_id in self.project_ids: try: request = self.client.regionInstanceGroupManagers().list( @@ -403,6 +402,18 @@ class Compute(GCPService): if zone_info.get("zone") ] + auto_healing_policies = [ + AutoHealingPolicy( + health_check=( + policy.get("healthCheck", "").split("/")[-1] + if policy.get("healthCheck") + else None + ), + initial_delay_sec=policy.get("initialDelaySec"), + ) + for policy in mig.get("autoHealingPolicies", []) + ] + self.instance_groups.append( ManagedInstanceGroup( name=mig.get("name", ""), @@ -413,6 +424,7 @@ class Compute(GCPService): is_regional=True, target_size=mig.get("targetSize", 0), project_id=project_id, + auto_healing_policies=auto_healing_policies, ) ) @@ -425,7 +437,6 @@ class Compute(GCPService): ) def _get_zonal_instance_groups(self, zone: str) -> None: - """Fetch zonal managed instance groups for all projects.""" for project_id in self.project_ids: try: request = self.client.instanceGroupManagers().list( @@ -441,6 +452,18 @@ class Compute(GCPService): mig_zone = mig.get("zone", zone).split("/")[-1] mig_region = mig_zone.rsplit("-", 1)[0] + auto_healing_policies = [ + AutoHealingPolicy( + health_check=( + policy.get("healthCheck", "").split("/")[-1] + if policy.get("healthCheck") + else None + ), + initial_delay_sec=policy.get("initialDelaySec"), + ) + for policy in mig.get("autoHealingPolicies", []) + ] + self.instance_groups.append( ManagedInstanceGroup( name=mig.get("name", ""), @@ -451,6 +474,7 @@ class Compute(GCPService): is_regional=False, target_size=mig.get("targetSize", 0), project_id=project_id, + auto_healing_policies=auto_healing_policies, ) ) @@ -585,6 +609,11 @@ class LoadBalancer(BaseModel): project_id: str +class AutoHealingPolicy(BaseModel): + health_check: Optional[str] = None + initial_delay_sec: Optional[int] = None + + class ManagedInstanceGroup(BaseModel): name: str id: str @@ -594,4 +623,5 @@ class ManagedInstanceGroup(BaseModel): is_regional: bool target_size: int project_id: str + auto_healing_policies: list[AutoHealingPolicy] = [] load_balanced: bool = False diff --git a/tests/providers/gcp/gcp_fixtures.py b/tests/providers/gcp/gcp_fixtures.py index 7706ec43aa..c6182bbf0c 100644 --- a/tests/providers/gcp/gcp_fixtures.py +++ b/tests/providers/gcp/gcp_fixtures.py @@ -1219,6 +1219,12 @@ def mock_api_instance_group_managers_calls(client: MagicMock): }, ] }, + "autoHealingPolicies": [ + { + "healthCheck": "https://www.googleapis.com/compute/v1/projects/test-project/global/healthChecks/http-health-check", + "initialDelaySec": 300, + } + ], }, { "name": "regional-mig-single-zone", @@ -1231,6 +1237,7 @@ def mock_api_instance_group_managers_calls(client: MagicMock): } ] }, + # No autoHealingPolicies - testing missing autohealing }, ] } @@ -1243,6 +1250,12 @@ def mock_api_instance_group_managers_calls(client: MagicMock): "name": "zonal-mig-1", "id": zonal_mig1_id, "targetSize": 2, + "autoHealingPolicies": [ + { + "healthCheck": "https://www.googleapis.com/compute/v1/projects/test-project/global/healthChecks/tcp-health-check", + "initialDelaySec": 120, + } + ], }, ] } diff --git a/tests/providers/gcp/services/compute/compute_instance_group_autohealing_enabled/compute_instance_group_autohealing_enabled_test.py b/tests/providers/gcp/services/compute/compute_instance_group_autohealing_enabled/compute_instance_group_autohealing_enabled_test.py new file mode 100644 index 0000000000..57de501921 --- /dev/null +++ b/tests/providers/gcp/services/compute/compute_instance_group_autohealing_enabled/compute_instance_group_autohealing_enabled_test.py @@ -0,0 +1,512 @@ +from re import search +from unittest import mock + +from prowler.providers.gcp.models import GCPProject +from tests.providers.gcp.gcp_fixtures import GCP_PROJECT_ID, set_mocked_gcp_provider + + +class Test_compute_instance_group_autohealing_enabled: + + def test_no_instance_groups(self): + compute_client = mock.MagicMock() + compute_client.project_ids = [GCP_PROJECT_ID] + compute_client.instance_groups = [] + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=set_mocked_gcp_provider(), + ), + mock.patch( + "prowler.providers.gcp.services.compute.compute_instance_group_autohealing_enabled.compute_instance_group_autohealing_enabled.compute_client", + new=compute_client, + ), + ): + from prowler.providers.gcp.services.compute.compute_instance_group_autohealing_enabled.compute_instance_group_autohealing_enabled import ( + compute_instance_group_autohealing_enabled, + ) + + check = compute_instance_group_autohealing_enabled() + result = check.execute() + assert len(result) == 0 + + def test_mig_with_autohealing_pass(self): + from prowler.providers.gcp.services.compute.compute_service import ( + AutoHealingPolicy, + ManagedInstanceGroup, + ) + + mig = ManagedInstanceGroup( + name="mig-with-autohealing", + id="123456789", + region="us-central1", + zone="us-central1-a", + zones=["us-central1-a"], + is_regional=False, + target_size=3, + project_id=GCP_PROJECT_ID, + auto_healing_policies=[ + AutoHealingPolicy( + health_check="http-health-check", + initial_delay_sec=300, + ) + ], + ) + + compute_client = mock.MagicMock() + compute_client.project_ids = [GCP_PROJECT_ID] + compute_client.instance_groups = [mig] + compute_client.projects = { + GCP_PROJECT_ID: GCPProject( + id=GCP_PROJECT_ID, + number="123456789012", + name="test-project", + labels={}, + lifecycle_state="ACTIVE", + ) + } + compute_client.region = "us-central1" + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=set_mocked_gcp_provider(), + ), + mock.patch( + "prowler.providers.gcp.services.compute.compute_instance_group_autohealing_enabled.compute_instance_group_autohealing_enabled.compute_client", + new=compute_client, + ), + ): + from prowler.providers.gcp.services.compute.compute_instance_group_autohealing_enabled.compute_instance_group_autohealing_enabled import ( + compute_instance_group_autohealing_enabled, + ) + + check = compute_instance_group_autohealing_enabled() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "PASS" + assert search( + f"Managed Instance Group {mig.name} has autohealing enabled with health check", + result[0].status_extended, + ) + assert "http-health-check" in result[0].status_extended + assert result[0].resource_id == mig.id + assert result[0].resource_name == mig.name + assert result[0].location == mig.region + assert result[0].project_id == GCP_PROJECT_ID + + def test_mig_without_autohealing_fail(self): + from prowler.providers.gcp.services.compute.compute_service import ( + ManagedInstanceGroup, + ) + + mig = ManagedInstanceGroup( + name="mig-no-autohealing", + id="987654321", + region="us-central1", + zone="us-central1-a", + zones=["us-central1-a"], + is_regional=False, + target_size=2, + project_id=GCP_PROJECT_ID, + auto_healing_policies=[], + ) + + compute_client = mock.MagicMock() + compute_client.project_ids = [GCP_PROJECT_ID] + compute_client.instance_groups = [mig] + compute_client.projects = { + GCP_PROJECT_ID: GCPProject( + id=GCP_PROJECT_ID, + number="123456789012", + name="test-project", + labels={}, + lifecycle_state="ACTIVE", + ) + } + compute_client.region = "us-central1" + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=set_mocked_gcp_provider(), + ), + mock.patch( + "prowler.providers.gcp.services.compute.compute_instance_group_autohealing_enabled.compute_instance_group_autohealing_enabled.compute_client", + new=compute_client, + ), + ): + from prowler.providers.gcp.services.compute.compute_instance_group_autohealing_enabled.compute_instance_group_autohealing_enabled import ( + compute_instance_group_autohealing_enabled, + ) + + check = compute_instance_group_autohealing_enabled() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "FAIL" + assert search( + f"Managed Instance Group {mig.name} does not have autohealing enabled", + result[0].status_extended, + ) + assert result[0].resource_id == mig.id + assert result[0].resource_name == mig.name + assert result[0].location == mig.region + assert result[0].project_id == GCP_PROJECT_ID + + def test_mig_with_autohealing_but_missing_health_check_fail(self): + from prowler.providers.gcp.services.compute.compute_service import ( + AutoHealingPolicy, + ManagedInstanceGroup, + ) + + mig = ManagedInstanceGroup( + name="mig-missing-health-check", + id="111222333", + region="europe-west1", + zone=None, + zones=["europe-west1-b", "europe-west1-c"], + is_regional=True, + target_size=2, + project_id=GCP_PROJECT_ID, + auto_healing_policies=[ + AutoHealingPolicy( + health_check=None, + initial_delay_sec=300, + ) + ], + ) + + compute_client = mock.MagicMock() + compute_client.project_ids = [GCP_PROJECT_ID] + compute_client.instance_groups = [mig] + compute_client.projects = { + GCP_PROJECT_ID: GCPProject( + id=GCP_PROJECT_ID, + number="123456789012", + name="test-project", + labels={}, + lifecycle_state="ACTIVE", + ) + } + compute_client.region = "europe-west1" + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=set_mocked_gcp_provider(), + ), + mock.patch( + "prowler.providers.gcp.services.compute.compute_instance_group_autohealing_enabled.compute_instance_group_autohealing_enabled.compute_client", + new=compute_client, + ), + ): + from prowler.providers.gcp.services.compute.compute_instance_group_autohealing_enabled.compute_instance_group_autohealing_enabled import ( + compute_instance_group_autohealing_enabled, + ) + + check = compute_instance_group_autohealing_enabled() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "FAIL" + assert search( + f"Managed Instance Group {mig.name} has autohealing configured but is missing a valid health check", + result[0].status_extended, + ) + assert result[0].resource_id == mig.id + assert result[0].resource_name == mig.name + assert result[0].location == mig.region + assert result[0].project_id == GCP_PROJECT_ID + + def test_regional_mig_with_autohealing_pass(self): + from prowler.providers.gcp.services.compute.compute_service import ( + AutoHealingPolicy, + ManagedInstanceGroup, + ) + + mig = ManagedInstanceGroup( + name="regional-mig-autohealing", + id="444555666", + region="us-east1", + zone=None, + zones=["us-east1-b", "us-east1-c", "us-east1-d"], + is_regional=True, + target_size=6, + project_id=GCP_PROJECT_ID, + auto_healing_policies=[ + AutoHealingPolicy( + health_check="tcp-health-check", + initial_delay_sec=120, + ) + ], + ) + + compute_client = mock.MagicMock() + compute_client.project_ids = [GCP_PROJECT_ID] + compute_client.instance_groups = [mig] + compute_client.projects = { + GCP_PROJECT_ID: GCPProject( + id=GCP_PROJECT_ID, + number="123456789012", + name="test-project", + labels={}, + lifecycle_state="ACTIVE", + ) + } + compute_client.region = "us-east1" + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=set_mocked_gcp_provider(), + ), + mock.patch( + "prowler.providers.gcp.services.compute.compute_instance_group_autohealing_enabled.compute_instance_group_autohealing_enabled.compute_client", + new=compute_client, + ), + ): + from prowler.providers.gcp.services.compute.compute_instance_group_autohealing_enabled.compute_instance_group_autohealing_enabled import ( + compute_instance_group_autohealing_enabled, + ) + + check = compute_instance_group_autohealing_enabled() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "PASS" + assert search( + f"Managed Instance Group {mig.name} has autohealing enabled", + result[0].status_extended, + ) + assert "tcp-health-check" in result[0].status_extended + assert result[0].resource_id == mig.id + assert result[0].resource_name == mig.name + assert result[0].location == mig.region + assert result[0].project_id == GCP_PROJECT_ID + + def test_multiple_migs_mixed_results(self): + from prowler.providers.gcp.services.compute.compute_service import ( + AutoHealingPolicy, + ManagedInstanceGroup, + ) + + mig_pass = ManagedInstanceGroup( + name="mig-good", + id="111", + region="us-central1", + zone="us-central1-a", + zones=["us-central1-a"], + is_regional=False, + target_size=2, + project_id=GCP_PROJECT_ID, + auto_healing_policies=[ + AutoHealingPolicy( + health_check="http-health-check", + initial_delay_sec=300, + ) + ], + ) + + mig_fail_no_autohealing = ManagedInstanceGroup( + name="mig-no-autohealing", + id="222", + region="us-central1", + zone="us-central1-b", + zones=["us-central1-b"], + is_regional=False, + target_size=1, + project_id=GCP_PROJECT_ID, + auto_healing_policies=[], + ) + + mig_fail_no_health_check = ManagedInstanceGroup( + name="mig-no-health-check", + id="333", + region="us-central1", + zone=None, + zones=["us-central1-a", "us-central1-b"], + is_regional=True, + target_size=3, + project_id=GCP_PROJECT_ID, + auto_healing_policies=[ + AutoHealingPolicy( + health_check=None, + initial_delay_sec=120, + ) + ], + ) + + compute_client = mock.MagicMock() + compute_client.project_ids = [GCP_PROJECT_ID] + compute_client.instance_groups = [ + mig_pass, + mig_fail_no_autohealing, + mig_fail_no_health_check, + ] + compute_client.projects = { + GCP_PROJECT_ID: GCPProject( + id=GCP_PROJECT_ID, + number="123456789012", + name="test-project", + labels={}, + lifecycle_state="ACTIVE", + ) + } + compute_client.region = "us-central1" + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=set_mocked_gcp_provider(), + ), + mock.patch( + "prowler.providers.gcp.services.compute.compute_instance_group_autohealing_enabled.compute_instance_group_autohealing_enabled.compute_client", + new=compute_client, + ), + ): + from prowler.providers.gcp.services.compute.compute_instance_group_autohealing_enabled.compute_instance_group_autohealing_enabled import ( + compute_instance_group_autohealing_enabled, + ) + + check = compute_instance_group_autohealing_enabled() + result = check.execute() + + assert len(result) == 3 + assert result[0].status == "PASS" + assert result[0].resource_id == mig_pass.id + assert result[1].status == "FAIL" + assert result[1].resource_id == mig_fail_no_autohealing.id + assert "does not have autohealing enabled" in result[1].status_extended + assert result[2].status == "FAIL" + assert result[2].resource_id == mig_fail_no_health_check.id + assert "missing a valid health check" in result[2].status_extended + + def test_mig_with_multiple_health_checks_pass(self): + from prowler.providers.gcp.services.compute.compute_service import ( + AutoHealingPolicy, + ManagedInstanceGroup, + ) + + mig = ManagedInstanceGroup( + name="mig-multiple-policies", + id="777888999", + region="asia-east1", + zone=None, + zones=["asia-east1-a", "asia-east1-b"], + is_regional=True, + target_size=4, + project_id=GCP_PROJECT_ID, + auto_healing_policies=[ + AutoHealingPolicy( + health_check="http-health-check-1", + initial_delay_sec=300, + ), + AutoHealingPolicy( + health_check="tcp-health-check-2", + initial_delay_sec=120, + ), + ], + ) + + compute_client = mock.MagicMock() + compute_client.project_ids = [GCP_PROJECT_ID] + compute_client.instance_groups = [mig] + compute_client.projects = { + GCP_PROJECT_ID: GCPProject( + id=GCP_PROJECT_ID, + number="123456789012", + name="test-project", + labels={}, + lifecycle_state="ACTIVE", + ) + } + compute_client.region = "asia-east1" + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=set_mocked_gcp_provider(), + ), + mock.patch( + "prowler.providers.gcp.services.compute.compute_instance_group_autohealing_enabled.compute_instance_group_autohealing_enabled.compute_client", + new=compute_client, + ), + ): + from prowler.providers.gcp.services.compute.compute_instance_group_autohealing_enabled.compute_instance_group_autohealing_enabled import ( + compute_instance_group_autohealing_enabled, + ) + + check = compute_instance_group_autohealing_enabled() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "PASS" + assert "http-health-check-1" in result[0].status_extended + assert "tcp-health-check-2" in result[0].status_extended + assert result[0].resource_id == mig.id + assert result[0].resource_name == mig.name + assert result[0].location == mig.region + assert result[0].project_id == GCP_PROJECT_ID + + def test_mig_with_empty_health_check_string_fail(self): + from prowler.providers.gcp.services.compute.compute_service import ( + AutoHealingPolicy, + ManagedInstanceGroup, + ) + + mig = ManagedInstanceGroup( + name="mig-empty-health-check", + id="999000111", + region="europe-north1", + zone="europe-north1-a", + zones=["europe-north1-a"], + is_regional=False, + target_size=1, + project_id=GCP_PROJECT_ID, + auto_healing_policies=[ + AutoHealingPolicy( + health_check="", + initial_delay_sec=300, + ) + ], + ) + + compute_client = mock.MagicMock() + compute_client.project_ids = [GCP_PROJECT_ID] + compute_client.instance_groups = [mig] + compute_client.projects = { + GCP_PROJECT_ID: GCPProject( + id=GCP_PROJECT_ID, + number="123456789012", + name="test-project", + labels={}, + lifecycle_state="ACTIVE", + ) + } + compute_client.region = "europe-north1" + + with ( + mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=set_mocked_gcp_provider(), + ), + mock.patch( + "prowler.providers.gcp.services.compute.compute_instance_group_autohealing_enabled.compute_instance_group_autohealing_enabled.compute_client", + new=compute_client, + ), + ): + from prowler.providers.gcp.services.compute.compute_instance_group_autohealing_enabled.compute_instance_group_autohealing_enabled import ( + compute_instance_group_autohealing_enabled, + ) + + check = compute_instance_group_autohealing_enabled() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "FAIL" + assert search( + f"Managed Instance Group {mig.name} has autohealing configured but is missing a valid health check", + result[0].status_extended, + ) diff --git a/tests/providers/gcp/services/compute/compute_service_test.py b/tests/providers/gcp/services/compute/compute_service_test.py index b8c1d1be19..4f8c0c0390 100644 --- a/tests/providers/gcp/services/compute/compute_service_test.py +++ b/tests/providers/gcp/services/compute/compute_service_test.py @@ -191,7 +191,6 @@ class TestComputeService: # We expect 3 MIGs: 2 regional (from region europe-west1-b) and 1 zonal (from zone1) assert len(compute_client.instance_groups) == 3 - # First regional MIG - multiple zones regional_mig_1 = next( ( mig @@ -211,8 +210,13 @@ class TestComputeService: assert regional_mig_1.is_regional assert regional_mig_1.target_size == 3 assert regional_mig_1.project_id == GCP_PROJECT_ID + assert len(regional_mig_1.auto_healing_policies) == 1 + assert ( + regional_mig_1.auto_healing_policies[0].health_check + == "http-health-check" + ) + assert regional_mig_1.auto_healing_policies[0].initial_delay_sec == 300 - # Second regional MIG - single zone regional_mig_2 = next( ( mig @@ -230,8 +234,8 @@ class TestComputeService: assert regional_mig_2.is_regional assert regional_mig_2.target_size == 1 assert regional_mig_2.project_id == GCP_PROJECT_ID + assert len(regional_mig_2.auto_healing_policies) == 0 - # Zonal MIG zonal_mig = next( ( mig @@ -251,3 +255,6 @@ class TestComputeService: assert not zonal_mig.is_regional assert zonal_mig.target_size == 2 assert zonal_mig.project_id == GCP_PROJECT_ID + assert len(zonal_mig.auto_healing_policies) == 1 + assert zonal_mig.auto_healing_policies[0].health_check == "tcp-health-check" + assert zonal_mig.auto_healing_policies[0].initial_delay_sec == 120