Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Add ACM Cert Handler #20

Merged
merged 2 commits into from
Oct 26, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions lambda_function/aws/resources/acm_cert_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import json
import logging
from concurrent.futures import ThreadPoolExecutor, as_completed

import boto3
import consts
from aws.resources.base_handler import BaseHandler
from port.entities import create_entities_json, handle_entities

logger = logging.getLogger(__name__)


class ACMHandler(BaseHandler):
def handle(self):
for region in list(self.regions):
aws_acm_client = boto3.client("acm", region_name=region)
try:
paginator = aws_acm_client.get_paginator('list_certificates')
for page in paginator.paginate():
certificate_summary_list = page.get('CertificateSummaryList', [])
self._handle_list_response(certificate_summary_list, region)

if self.lambda_context.get_remaining_time_in_millis() < consts.REMAINING_TIME_TO_REINVOKE_THRESHOLD:
# Lambda timeout is too close, should return checkpoint for next run
return self._handle_close_to_timeout(region)
except Exception as e:
logger.error(f"Failed to list ACM certificates in region: {region}; {e}")
self.skip_delete = True

return {
"aws_entities": self.aws_entities,
"next_resource_config": None,
"skip_delete": self.skip_delete,
}

def _handle_list_response(self, certificate_summary_list, region):
with ThreadPoolExecutor(max_workers=consts.MAX_CC_WORKERS) as executor:
futures = [
executor.submit(self.handle_single_resource_item, region, cert.get("CertificateArn", ""))
for cert in certificate_summary_list
]
for completed_future in as_completed(futures):
result = completed_future.result()
self.aws_entities.update(result.get("aws_entities", set()))
self.skip_delete = result.get("skip_delete", False) if not self.skip_delete else self.skip_delete

def _handle_close_to_timeout(self, region):
if "selector" not in self.resource_config:
self.resource_config["selector"] = {}
self.resource_config["selector"]["aws"] = self.selector_aws

return {
"aws_entities": self.aws_entities,
"next_resource_config": self.resource_config,
"skip_delete": self.skip_delete,
}

def handle_single_resource_item(self, region, certificate_arn, action_type="upsert"):
entities = []
skip_delete = False
try:
resource_obj = {}
if action_type == "upsert":
logger.info(f"Get ACM certificate details for ARN: {certificate_arn}")
aws_acm_client = boto3.client("acm", region_name=region)
response = aws_acm_client.describe_certificate(CertificateArn=certificate_arn)
resource_obj = response.get("Certificate", {})

# Handles unserializable date properties in the JSON by turning them into a string
resource_obj = json.loads(json.dumps(resource_obj, default=str))
elif action_type == "delete":
resource_obj = {"identifier": certificate_arn} # Entity identifier to delete
entities = create_entities_json(resource_obj, self.selector_query, self.mappings, action_type)
except Exception as e:
logger.error(f"Failed to extract or transform certificate ARN: {certificate_arn}, error: {e}")
skip_delete = True

aws_entities = handle_entities(entities, self.port_client, action_type)

return {"aws_entities": aws_entities, "skip_delete": skip_delete}
9 changes: 7 additions & 2 deletions lambda_function/aws/resources/handler_creator.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,14 @@
from aws.resources.cloudformation_handler import CloudFormationHandler
from aws.resources.ec2_instance_handler import EC2InstanceHandler
from aws.resources.load_balancer_handler import LoadBalancerHandler
from aws.resources.acm_cert_handler import ACMHandler

SPECIAL_AWS_HANDLERS: Dict[str, Type[BaseHandler]] = {"AWS::CloudFormation::Stack": CloudFormationHandler, "AWS::EC2::Instance": EC2InstanceHandler,
"AWS::ElasticLoadBalancingV2::LoadBalancer": LoadBalancerHandler}
SPECIAL_AWS_HANDLERS: Dict[str, Type[BaseHandler]] = {
"AWS::CloudFormation::Stack": CloudFormationHandler,
"AWS::EC2::Instance": EC2InstanceHandler,
"AWS::ElasticLoadBalancingV2::LoadBalancer": LoadBalancerHandler,
"AWS::ACM::Certificate": ACMHandler,
}


def create_resource_handler(resource_config, port_client, lambda_context, default_region):
Expand Down