def describe_stack_events(stack_name, session=None): """Performs describe_stack_events on the provided stack name http://boto3.readthedocs.io/en/latest/reference/services/cloudformation.html#CloudFormation.Client.describe_stack_events Args: stack_name (str): Name of the stack to describe session (object, optional): boto3 session object Returns: dict: Standard AWS dictionary with stack event details """ client = boto3_client(service='cloudformation', session=session) try: response = client.describe_stack_events(StackName=stack_name) return response except ex.ClientError as e: if str(e).endswith(" does not exist"): raise ex.NotFoundException( f"Could not find stack with name {stack_name}") else: raise ex.ClientError( f"Failed to lookup stack events for {stack_name}: {str(e)}")
def disable_termination_protection(stack_name, session=None): """Disables Termination Protection on CloudFormation Stacks Args: stack_name (str): Passing key word arguments that will be used to create the session (object, optional): boto3 session object Returns: none """ try: client = boto3_client(service='cloudformation', session=session) stack_exists = describe_stack(stack_name=stack_name, session=session) if stack_exists: logger.info( "Checking time difference between Stack Creation and now. (disable if < 20 min)" ) diff = datetime.now( timezone.utc) - stack_exists['Stacks'][0]['CreationTime'] if stack_exists and (divmod(diff.days * 86400 + diff.seconds, 60)[0] < 20): logger.info( f"Disabling Termination Protection on {stack_name}") response = client.update_termination_protection( EnableTerminationProtection=False, StackName=stack_name) logger.debug(response) else: logger.warning('Time difference is greater than 20 min') except Exception as e: logger.error(str(e)) raise Exception
def list_stacks(session=None): """Gets a list of all CloudFormation stacks in the account http://boto3.readthedocs.io/en/latest/reference/services/cloudformation.html#CloudFormation.Client.list_stacks Args: session (object, optional): boto3 session object Returns: list of str: List of stack names in the account """ stacks = [] client = boto3_client(service='cloudformation', session=session) try: paginator = client.get_paginator("list_stacks") for page in paginator.paginate(StackStatusFilter=[ 'CREATE_IN_PROGRESS', 'CREATE_FAILED', 'CREATE_COMPLETE', 'ROLLBACK_IN_PROGRESS', 'ROLLBACK_FAILED', 'ROLLBACK_COMPLETE', 'DELETE_IN_PROGRESS', 'DELETE_FAILED', 'UPDATE_IN_PROGRESS', 'UPDATE_COMPLETE_CLEANUP_IN_PROGRESS', 'UPDATE_COMPLETE', 'UPDATE_ROLLBACK_IN_PROGRESS', 'UPDATE_ROLLBACK_FAILED', 'UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS', 'UPDATE_ROLLBACK_COMPLETE', 'REVIEW_IN_PROGRESS' ]): for x in page['StackSummaries']: stacks.append(x['StackName']) except Exception as e: raise ex.CloudFormationException( f"Failed to get list of cloudformation templates: {str(e)}") return stacks
def update_stack(**kwargs): """Updates an existing stack with new template http://boto3.readthedocs.io/en/latest/reference/services/cloudformation.html#CloudFormation.Client.update_stack Args: stack_name (str): Name of the stack to update template (str): Body of a CFN template file, should already have been read in using general_helper.load_file(file) capability (str): The capability string noting if the stack contains IAM or custom named IAM resources Options: 'CAPABILITY_IAM'|'CAPABILITY_NAMED_IAM' params (list of dict, optional): List of parameter structures that specify input parameters for the stack tags (list): tags set on cloudformation stack session (object, optional): boto3 session object Returns: dict: Standard AWS dictionary with stack update results """ logger.info(f"Arguments:{kwargs}") logger.info(f"Updating Stack:{kwargs['StackName']}") client = boto3_client(service='cloudformation', session=kwargs['session']) del kwargs['session'] try: response = client.update_stack(**kwargs) except Exception as e: logger.warning(e) if re.search(r'(No updates are to be performed)', str(e)): logger.warning("No updates need to be performed...") response = False else: raise e return response
def describe_stack(stack_name, session=None): """Performs describe_stack on the provided stack name http://boto3.readthedocs.io/en/latest/reference/services/cloudformation.html#CloudFormation.Client.describe_stacks Args: stack_name (str): Name of the stack to describe session (object, optional): boto3 session object Returns: dict: Standard AWS dictionary with stack details """ client = boto3_client(service='cloudformation', session=session) try: logger.info(f"Getting details about CloudFormation Stack:{stack_name}") response = client.describe_stacks(StackName=stack_name) return response except ex.ClientError as e: if str(e).endswith(" does not exist"): logger.warning(f"Stack, {stack_name} does not exist...") # return False else: raise ex.ClientError( f"Failed to lookup stack {stack_name}: {str(e)}") except Exception as e: logger.warning(f"describe_stack error:{str(e)}")
def delete_stack(stack_name: str, session=None): """Deletes the provided stack name http://boto3.readthedocs.io/en/latest/reference/services/cloudformation.html#CloudFormation.Client.delete_stack Args: stack_name (str): Name of the stack to delete session (object, optional): boto3 session object Returns: dict: Standard AWS dictionary with stack deletion results """ client = boto3_client(service='cloudformation', session=session) response = client.delete_stack(StackName=stack_name) return response
def enable_termination_protection(stack_name, session=None): """Enables Termination Protection on CloudFormation Stacks Args: stack_name (str): Passing key word arguments that will be used to create the session (object, optional): boto3 session object Returns: none """ logger.info(f"Setting Termination Protection on {stack_name}") try: client = boto3_client(service='cloudformation', session=session) response = client.update_termination_protection( EnableTerminationProtection=True, StackName=stack_name) logger.debug(response) except Exception as e: logger.error(str(e)) raise Exception
def validate_template(template, session=None): """Performs template validation on the provided template body. http://boto3.readthedocs.io/en/latest/reference/services/cloudformation.html#CloudFormation.Client.validate_template Args: template (str): Body of a CFN template file, should already have been read in using helper.load_file(file) session (object, optional): boto3 session object Returns: dict: Standard AWS dictionary with validation results, raises exception if template is invalid """ logger.info("Validating CloudFormation Template") client = boto3_client(service='cloudformation', session=session) try: response = client.validate_template(TemplateBody=template) return response except ex.ClientError as e: raise ex.ClientError( f"CloudFormation template validation failed: {str(e)}")
def get_stack_waiter(event, session=None): """Gets an object that can wait for some stack condition to be true http://boto3.readthedocs.io/en/latest/reference/services/cloudformation.html#CloudFormation.Client.get_waiter Args: event (str): Name of the event to wait for IE: 'stack_create_complete' session (object, optional): boto3 session object Returns: :obj:`boto3.waiter.Waiter`: Waiter object """ client = boto3_client(service='cloudformation', session=session) try: waiter = client.get_waiter(event) except BaseException as e: raise ex.StackWaiterException( f"Failed to retrieve stack waiter for event {event}: {str(e)}") return waiter
def assume_role_arn(role_arn, role_session_name=function_name, profile=None): """Assumes the provided role name in the provided account number http://boto3.readthedocs.io/en/latest/reference/services/sts.html#STS.Client.assume_role Args: role_arn (str): Arn of the IAM Role to assume role_session_name (str, optional): The name you'd like to use for the session (suggested to use the lambda function name) profile (str, optional): Local AWS Profile name Returns: dict: Returns standard AWS dictionary with credential details """ logger.info(f"Assuming Role:{role_arn}") sts_client = boto3_client(service='sts', profile=profile) assumed_role_object = sts_client.assume_role( RoleArn=role_arn, RoleSessionName=role_session_name) assumed_credentials = assumed_role_object['Credentials'] return assumed_credentials
def create_stack(**kwargs): """Creates a cloudformation stack using the provided parameters http://boto3.readthedocs.io/en/latest/reference/services/cloudformation.html#CloudFormation.Client.create_stack Args: stack_name (str): Name of the stack to create template (str): Body of a CFN template file, should already have been read in using general_helper.load_file(file) capability (str): The capability string noting if the stack contains IAM or custom named IAM resources Options: 'CAPABILITY_IAM'|'CAPABILITY_NAMED_IAM' params (list of dict, optional): List of parameter structures that specify input parameters for the stack tags (list): tags set on cloudformation stack session (object, optional): boto3 session object Returns: dict: Standard AWS dictionary with create_stack results """ logger.info(f"Arguments:{kwargs}") logger.info(f"Creating Stack:{kwargs['StackName']}") client = boto3_client(service='cloudformation', session=kwargs['session']) del kwargs['session'] response = client.create_stack(**kwargs) return response