diff --git a/prowler/providers/aws/services/autoscaling/autoscaling_group_capacity_rebalance_enabled/__init__.py b/prowler/providers/aws/services/autoscaling/autoscaling_group_capacity_rebalance_enabled/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/prowler/providers/aws/services/autoscaling/autoscaling_group_capacity_rebalance_enabled/autoscaling_group_capacity_rebalance_enabled.metadata.json b/prowler/providers/aws/services/autoscaling/autoscaling_group_capacity_rebalance_enabled/autoscaling_group_capacity_rebalance_enabled.metadata.json new file mode 100644 index 0000000000..4436b7affe --- /dev/null +++ b/prowler/providers/aws/services/autoscaling/autoscaling_group_capacity_rebalance_enabled/autoscaling_group_capacity_rebalance_enabled.metadata.json @@ -0,0 +1,34 @@ +{ + "Provider": "aws", + "CheckID": "autoscaling_group_capacity_rebalance_enabled", + "CheckTitle": "Check if Amazon EC2 Auto Scaling groups have capacity rebalance enabled.", + "CheckType": [ + "Resiliance" + ], + "ServiceName": "autoscaling", + "SubServiceName": "", + "ResourceIdTemplate": "arn:aws:autoscaling:region:account-id:autoScalingGroup/autoScalingGroupName", + "Severity": "medium", + "ResourceType": "AwsAutoScalingAutoScalingGroup", + "Description": "This control checks whether an Amazon EC2 Auto Scaling group has capacity rebalance enabled.", + "Risk": "When you don't use Capacity Rebalancing, Amazon EC2 Auto Scaling doesn't replace Spot Instances until after the Amazon EC2 Spot service interrupts the instances and their health check fails. Before interrupting an instance, Amazon EC2 always gives both an EC2 instance rebalance recommendation and a Spot two-minute instance interruption notice.", + "RelatedUrl": "https://docs.aws.amazon.com/autoscaling/ec2/userguide/ec2-auto-scaling-capacity-rebalancing.html", + "Remediation": { + "Code": { + "CLI": "aws autoscaling create-auto-scaling-group --capacity-rebalance", + "NativeIaC": "", + "Other": "https://docs.aws.amazon.com/autoscaling/ec2/userguide/enable-capacity-rebalancing-console-cli.html", + "Terraform": "" + }, + "Recommendation": { + "Text": "When you enable Capacity Rebalancing for your Auto Scaling group, Amazon EC2 Auto Scaling attempts to proactively replace the Spot Instances in your group that have received a rebalance recommendation. This provides an opportunity to rebalance your workload to new Spot Instances that aren't at an elevated risk of interruption.", + "Url": "https://docs.aws.amazon.com/awssupport/latest/user/fault-tolerance-checks.html#amazon-ec2-auto-scaling-group-capacity-rebalance-enabled" + } + }, + "Categories": [ + "redundancy" + ], + "DependsOn": [], + "RelatedTo": [], + "Notes": "" +} diff --git a/prowler/providers/aws/services/autoscaling/autoscaling_group_capacity_rebalance_enabled/autoscaling_group_capacity_rebalance_enabled.py b/prowler/providers/aws/services/autoscaling/autoscaling_group_capacity_rebalance_enabled/autoscaling_group_capacity_rebalance_enabled.py new file mode 100644 index 0000000000..da0822fb76 --- /dev/null +++ b/prowler/providers/aws/services/autoscaling/autoscaling_group_capacity_rebalance_enabled/autoscaling_group_capacity_rebalance_enabled.py @@ -0,0 +1,25 @@ +from prowler.lib.check.models import Check, Check_Report_AWS +from prowler.providers.aws.services.autoscaling.autoscaling_client import ( + autoscaling_client, +) + + +class autoscaling_group_capacity_rebalance_enabled(Check): + def execute(self): + findings = [] + for group in autoscaling_client.groups: + if group.load_balancers and group.target_groups: + report = Check_Report_AWS(self.metadata()) + report.region = group.region + report.resource_id = group.name + report.resource_arn = group.arn + report.resource_tags = group.tags + report.status = "FAIL" + report.status_extended = f"Autoscaling group {group.name} does not have capacity rebalance enabled." + if group.capacity_rebalance: + report.status = "PASS" + report.status_extended = f"Autoscaling group {group.name} has capacity rebalance enabled." + + findings.append(report) + + return findings diff --git a/prowler/providers/aws/services/autoscaling/autoscaling_service.py b/prowler/providers/aws/services/autoscaling/autoscaling_service.py index b207b89d2a..fe2671f88b 100644 --- a/prowler/providers/aws/services/autoscaling/autoscaling_service.py +++ b/prowler/providers/aws/services/autoscaling/autoscaling_service.py @@ -85,6 +85,9 @@ class AutoScaling(AWSService): tags=group.get("Tags"), instance_types=instance_types, az_instance_types=az_instance_types, + capacity_rebalance=group.get( + "CapacityRebalance", False + ), launch_template=group.get("LaunchTemplate", {}), mixed_instances_policy_launch_template=group.get( "MixedInstancesPolicy", {} @@ -168,6 +171,7 @@ class Group(BaseModel): tags: list = [] instance_types: list = [] az_instance_types: dict = {} + capacity_rebalance: bool launch_template: dict = {} mixed_instances_policy_launch_template: dict = {} health_check_type: str diff --git a/tests/providers/aws/services/autoscaling/autoscaling_group_capacity_rebalance_enabled/autoscaling_group_capacity_rebalance_enabled_test.py b/tests/providers/aws/services/autoscaling/autoscaling_group_capacity_rebalance_enabled/autoscaling_group_capacity_rebalance_enabled_test.py new file mode 100644 index 0000000000..fc06c75dea --- /dev/null +++ b/tests/providers/aws/services/autoscaling/autoscaling_group_capacity_rebalance_enabled/autoscaling_group_capacity_rebalance_enabled_test.py @@ -0,0 +1,194 @@ +from unittest import mock + +from boto3 import client +from moto import mock_aws + +from tests.providers.aws.utils import AWS_REGION_US_EAST_1, set_mocked_aws_provider + + +class Test_autoscaling_group_capacity_rebalance_enabled: + @mock_aws + def test_no_autoscaling(self): + autoscaling_client = client("autoscaling", region_name=AWS_REGION_US_EAST_1) + autoscaling_client.groups = [] + + from prowler.providers.aws.services.autoscaling.autoscaling_service import ( + AutoScaling, + ) + + aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ), mock.patch( + "prowler.providers.aws.services.autoscaling.autoscaling_group_capacity_rebalance_enabled.autoscaling_group_capacity_rebalance_enabled.autoscaling_client", + new=AutoScaling(aws_provider), + ): + # Test Check + from prowler.providers.aws.services.autoscaling.autoscaling_group_capacity_rebalance_enabled.autoscaling_group_capacity_rebalance_enabled import ( + autoscaling_group_capacity_rebalance_enabled, + ) + + check = autoscaling_group_capacity_rebalance_enabled() + result = check.execute() + + assert len(result) == 0 + + @mock_aws + def test_groups_capacity_rebalance_enabled(self): + autoscaling_client = client("autoscaling", region_name=AWS_REGION_US_EAST_1) + autoscaling_client.create_launch_configuration( + LaunchConfigurationName="test", + ImageId="ami-12c6146b", + InstanceType="t1.micro", + KeyName="the_keys", + SecurityGroups=["default", "default2"], + ) + + ec2_client = client("ec2", region_name=AWS_REGION_US_EAST_1) + vpc_id = ec2_client.create_vpc(CidrBlock="10.0.0.0/16")["Vpc"]["VpcId"] + + elastic_load_balancer = client("elbv2", region_name=AWS_REGION_US_EAST_1) + target_group = elastic_load_balancer.create_target_group( + Name="my-target-group", + Protocol="HTTP", + Port=80, + VpcId=vpc_id, + HealthCheckProtocol="HTTP", + HealthCheckPort="80", + HealthCheckPath="/", + HealthCheckIntervalSeconds=30, + HealthCheckTimeoutSeconds=5, + HealthyThresholdCount=5, + UnhealthyThresholdCount=2, + Matcher={"HttpCode": "200"}, + ) + + autoscaling_group_name = "my-autoscaling-group" + autoscaling_client.create_auto_scaling_group( + AutoScalingGroupName=autoscaling_group_name, + LaunchConfigurationName="test", + MinSize=0, + MaxSize=0, + DesiredCapacity=0, + AvailabilityZones=["us-east-1a", "us-east-1b"], + HealthCheckType="ELB", + CapacityRebalance=True, + LoadBalancerNames=["my-load-balancer"], + TargetGroupARNs=[target_group["TargetGroups"][0]["TargetGroupArn"]], + ) + + autoscaling_group_arn = autoscaling_client.describe_auto_scaling_groups( + AutoScalingGroupNames=[autoscaling_group_name] + )["AutoScalingGroups"][0]["AutoScalingGroupARN"] + + from prowler.providers.aws.services.autoscaling.autoscaling_service import ( + AutoScaling, + ) + + aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ), mock.patch( + "prowler.providers.aws.services.autoscaling.autoscaling_group_capacity_rebalance_enabled.autoscaling_group_capacity_rebalance_enabled.autoscaling_client", + new=AutoScaling(aws_provider), + ): + # Test Check + from prowler.providers.aws.services.autoscaling.autoscaling_group_capacity_rebalance_enabled.autoscaling_group_capacity_rebalance_enabled import ( + autoscaling_group_capacity_rebalance_enabled, + ) + + check = autoscaling_group_capacity_rebalance_enabled() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "PASS" + assert ( + result[0].status_extended + == f"Autoscaling group {autoscaling_group_name} has capacity rebalance enabled." + ) + assert result[0].resource_id == autoscaling_group_name + assert result[0].resource_arn == autoscaling_group_arn + assert result[0].region == AWS_REGION_US_EAST_1 + assert result[0].resource_tags == [] + + @mock_aws + def test_groups_with_capacity_rebalance_disabled(self): + autoscaling_client = client("autoscaling", region_name=AWS_REGION_US_EAST_1) + autoscaling_client.create_launch_configuration( + LaunchConfigurationName="test", + ImageId="ami-12c6146b", + InstanceType="t1.micro", + KeyName="the_keys", + SecurityGroups=["default", "default2"], + ) + + ec2_client = client("ec2", region_name=AWS_REGION_US_EAST_1) + vpc_id = ec2_client.create_vpc(CidrBlock="10.0.0.0/16")["Vpc"]["VpcId"] + + elastic_load_balancer = client("elbv2", region_name=AWS_REGION_US_EAST_1) + target_group = elastic_load_balancer.create_target_group( + Name="my-target-group", + Protocol="HTTP", + Port=80, + VpcId=vpc_id, + HealthCheckProtocol="HTTP", + HealthCheckPort="80", + HealthCheckPath="/", + HealthCheckIntervalSeconds=30, + HealthCheckTimeoutSeconds=5, + HealthyThresholdCount=5, + UnhealthyThresholdCount=2, + Matcher={"HttpCode": "200"}, + ) + + autoscaling_group_name = "my-autoscaling-group" + autoscaling_client.create_auto_scaling_group( + AutoScalingGroupName=autoscaling_group_name, + LaunchConfigurationName="test", + MinSize=0, + MaxSize=0, + DesiredCapacity=0, + AvailabilityZones=["us-east-1a"], + HealthCheckType="EC2", + LoadBalancerNames=["my-load-balancer"], + TargetGroupARNs=[target_group["TargetGroups"][0]["TargetGroupArn"]], + ) + + autoscaling_group_arn = autoscaling_client.describe_auto_scaling_groups( + AutoScalingGroupNames=[autoscaling_group_name] + )["AutoScalingGroups"][0]["AutoScalingGroupARN"] + + from prowler.providers.aws.services.autoscaling.autoscaling_service import ( + AutoScaling, + ) + + aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1]) + + with mock.patch( + "prowler.providers.common.provider.Provider.get_global_provider", + return_value=aws_provider, + ), mock.patch( + "prowler.providers.aws.services.autoscaling.autoscaling_group_capacity_rebalance_enabled.autoscaling_group_capacity_rebalance_enabled.autoscaling_client", + new=AutoScaling(aws_provider), + ): + # Test Check + from prowler.providers.aws.services.autoscaling.autoscaling_group_capacity_rebalance_enabled.autoscaling_group_capacity_rebalance_enabled import ( + autoscaling_group_capacity_rebalance_enabled, + ) + + check = autoscaling_group_capacity_rebalance_enabled() + result = check.execute() + + assert len(result) == 1 + assert result[0].status == "FAIL" + assert ( + result[0].status_extended + == f"Autoscaling group {autoscaling_group_name} does not have capacity rebalance enabled." + ) + assert result[0].resource_id == autoscaling_group_name + assert result[0].resource_tags == [] + assert result[0].resource_arn == autoscaling_group_arn diff --git a/tests/providers/aws/services/autoscaling/autoscaling_service_test.py b/tests/providers/aws/services/autoscaling/autoscaling_service_test.py index dcbd208842..290f70eae1 100644 --- a/tests/providers/aws/services/autoscaling/autoscaling_service_test.py +++ b/tests/providers/aws/services/autoscaling/autoscaling_service_test.py @@ -27,6 +27,7 @@ def mock_make_api_call(self, operation_name, kwarg): "AutoScalingGroupName": "my-autoscaling-group", "AutoScalingGroupARN": "arn:aws:autoscaling:us-east-1:123456789012:autoScalingGroup:uuid:autoScalingGroupName/my-autoscaling-group", "AvailabilityZones": ["us-east-1a", "us-east-1b"], + "CapacityRebalance": True, "Tags": [ { "Key": "tag_test", @@ -176,6 +177,7 @@ class Test_AutoScaling_Service: MaxSize=0, DesiredCapacity=0, AvailabilityZones=["us-east-1a", "us-east-1b"], + CapacityRebalance=True, Tags=[ { "Key": "tag_test", @@ -196,6 +198,7 @@ class Test_AutoScaling_Service: "us-east-1a", "us-east-1b", ] + assert autoscaling.groups[0].capacity_rebalance assert autoscaling.groups[0].tags == [ { "Key": "tag_test",