def delete_templates(self): """ Deletes cross-account role and configuration templates :return: """ s3_client = get_client_with_retries("s3", ["delete_object"], context=self.context) s3_key = "" try: for action_name in actions.all_actions(): action_properties = actions.get_action_properties(action_name) if not action_properties.get(actions.ACTION_INTERNAL, False): self._logger.info(ERR_DELETING_ACTION_TEMPLATE, action_name) s3_key = S3_CONFIGURATION_TEMPLATE.format(action_name) s3_client.delete_object_with_retries( Bucket=self.configuration_bucket, Key=s3_key) s3_key = S3_ROLES_TEMPLATE.format(action_name) s3_client.delete_object_with_retries( Bucket=self.configuration_bucket, Key=s3_key) except Exception as ex: self._logger.error(ERR_DELETE_TEMPLATE_, s3_key, str(ex)) try: self._logger.info(INF_DELETE_ALL_ACTIONS_TEMPLATE, s3_key) s3_key = S3_ROLES_TEMPLATE.format(ALL_ACTIONS_TEMPLATE_NAME) s3_client.delete_object_with_retries( Bucket=self.configuration_bucket, Key=s3_key) except Exception as ex: self._logger.error(ERR_DELETE_TEMPLATE_, s3_key, str(ex))
def generate_html_actions_page(html_file, region): with open(html_file) as f: html_template = "".join(f.readlines()) bucket = os.getenv(handlers.ENV_CONFIG_BUCKET) stack = os.getenv(handlers.ENV_STACK_NAME) action_groups = {} for a in actions.all_actions(): ap = actions.get_action_properties(a) if ap.get(actions.ACTION_INTERNAL): continue href = CFN_CONSOLE_URL_TEMPLATE.format( region, region, urllib.parse.quote(ap.get(actions.PARAM_DESCRIPTION, "")), region, bucket, a) group_name = group_name_from_action_name(a) if group_name not in action_groups: action_groups[group_name] = {} action_groups[group_name][a] = (href, ap.get(actions.ACTION_TITLE)) action_list = "" for g in sorted(action_groups.keys()): actions_list = "" for a in sorted(action_groups[g].keys()): actions_list += HTML_ACTION_LIST_ITEM.format( action_groups[g][a][0], action_groups[g][a][1]) action_list += HTML_GROUP_LIST_ITEM.format(g, actions_list) action_list = HTML_ACTIONS_GROUPS_LISTS.format(action_list) return html_template.replace("%actions%", action_list).replace("%stack%", stack)
def delete_templates(self): s3_client = get_client_with_retries("s3", ["delete_object"], context=self.context) s3_key = "" try: for action_name in actions.all_actions(): action_properties = actions.get_action_properties(action_name) if not action_properties.get(actions.ACTION_INTERNAL, False): self._logger.info(INF_DELETING_ACTION_TEMPLATE, action_name) s3_key = S3_KEY_ACTION_CONFIGURATION_TEMPLATE.format( action_name) s3_client.delete_object_with_retries( Bucket=self.configuration_bucket, Key=s3_key) except Exception as ex: self._logger.error(ERR_DELETE_CONFIG_ITEM, s3_key, self.configuration_bucket, str(ex)) self._logger.info(INF_DELETE_ALL_ACTIONS_TEMPLATE) for key in [ S3_KEY_ACTIONS_HTML_PAGE, S3_KEY_ACCOUNT_CONFIG_WITH_PARAMS, S3_KEY_ACCOUNT_CONFIG_CREATE_ALL, S3_KEY_ACCOUNT_EVENTS_FORWARD_TEMPLATE ]: try: s3_client.delete_object_with_retries( Bucket=self.configuration_bucket, Key=key) except Exception as ex: self._logger.error(ERR_DELETE_CONFIG_ITEM, key, self.configuration_bucket, str(ex))
def main(template_file, version, bucket): template = get_versioned_template(template_file, version, bucket) all_actions = actions.all_actions() add_actions_permissions(template, all_actions) add_additional_lambda_functions(template, all_actions) add_action_stack_resources(template, all_actions) print(json.dumps(template, indent=4))
def get_actions(context=None): """ Returns a dictionary with all available actions, see get_action for details on returned items :param context: Lambda context :return: all available action """ with _get_logger(context=context) as logger: logger.info("get_actions") return { action_name: get_action(action_name, log_this_call=False) for action_name in actions.all_actions() }
def __init__(self, context=None, logger=None): """ Initializes the instance :param context: Lambda context :param logger: Optional logger for warnings, if None then warnings are printed to console """ self._logger = logger self._this_account = None self._context = context self._all_timezones = {tz.lower(): tz for tz in pytz.all_timezones} self._all_actions = actions.all_actions() self._s3_client = None self._s3_configured_cross_account_roles = None
def get_action(name, context=None, log_this_call=True): """ Gets the details of the specified action :param name: Name of the action, raises an exception if the action does not exist :param context: Lambda context :param log_this_call: switch :return: Details of the specified action. """ with _get_logger(context=context) as logger: if log_this_call: logger.info("get_action") all_actions = actions.all_actions() if name not in all_actions: raise_value_error(ERR_ACTION_DOES_NOT_EXIST, name, ",".join(all_actions)) return actions.get_action_properties(name)
def generate_templates(self): """ Generates configuration and cross-account role templates :return: """ def generate_configuration_template(s3, builder, action): configuration_template = S3_KEY_ACTION_CONFIGURATION_TEMPLATE.format(action) self._logger.info(INF_CREATE_ACTION_TEMPLATE, action, configuration_template) template = json.dumps(builder.build_template(action), indent=3) s3.put_object_with_retries(Body=template, Bucket=self.configuration_bucket, Key=configuration_template) def generate_all_actions_cross_account_role_template_parameterized(s3, builder, all_act, template_description): self._logger.info(INF_CREATE_ALL_ACTIONS_CROSS_ROLES_TEMPLATE, S3_KEY_ACCOUNT_CONFIG_WITH_PARAMS) template = builder.build_template(action_list=all_act, description=template_description, with_conditional_params=True) if self.optimize_cross_account_template: template = CrossAccountRoleBuilder.compress_template(template) template_json = json.dumps(template, indent=3) s3.put_object_with_retries(Body=template_json, Bucket=self.configuration_bucket, Key=S3_KEY_ACCOUNT_CONFIG_WITH_PARAMS) # noinspection PyUnusedLocal def generate_all_actions_cross_account_role_template(s3, builder, all_act, template_description): self._logger.info(INF_CREATE_ALL_ACTIONS_CROSS_ROLES_TEMPLATE, S3_KEY_ACCOUNT_CONFIG_CREATE_ALL) template = json.dumps( builder.build_template(action_list=all_act, description=template_description, with_conditional_params=False), indent=3) s3.put_object_with_retries(Body=template, Bucket=self.configuration_bucket, Key=S3_KEY_ACCOUNT_CONFIG_CREATE_ALL) def generate_forward_events_template(s3): self._logger.info(INF_CREATE_EVENT_FORWARD_TEMPLATE, S3_KEY_ACCOUNT_EVENTS_FORWARD_TEMPLATE) template = build_events_forward_template(template_filename="./cloudformation/{}".format(FORWARD_EVENTS_TEMPLATE), script_filename="./forward-events.py", stack=self.stack_name, event_role_arn=self.events_forward_role, ops_automator_topic_arn=self.ops_automator_topic_arn, version=self.stack_version) s3.put_object_with_retries(Body=template, Bucket=self.configuration_bucket, Key=S3_KEY_ACCOUNT_EVENTS_FORWARD_TEMPLATE) def generate_scenario_templates(s3): self._logger.info("Creating task scenarios templates") for template_name, template in list(builders.build_scenario_templates(templates_dir="./cloudformation/scenarios", stack=self.stack_name)): self._logger.info(INF_SCENARIO_TEMPLATE, template_name, S3_KEY_SCENARIO_TEMPLATE_BUCKET) s3.put_object_with_retries(Body=template, Bucket=self.configuration_bucket, Key=S3_KEY_SCENARIO_TEMPLATE_KEY.format(template_name)) def generate_custom_resource_builder(s3): self._logger.info("Create custom resource builder script {}", S3_KEY_CUSTOM_RESOURCE_BUILDER) with open("./build_task_custom_resource.py", "rt") as f: script_text = "".join(f.readlines()) script_text = script_text.replace("%stack%", self.stack_name) script_text = script_text.replace("%account%", self.account) script_text = script_text.replace("%region%", self.region) script_text = script_text.replace("%config_table%", os.getenv("CONFIG_TABLE")) s3.put_object_with_retries(Body=script_text, Bucket=self.configuration_bucket, Key=S3_KEY_CUSTOM_RESOURCE_BUILDER) def generate_actions_html_page(s3): self._logger.info("Generating Actions HTML page {}", S3_KEY_ACTIONS_HTML_PAGE) html = builders.generate_html_actions_page(html_file="./builders/actions.html", region=self.region) s3.put_object_with_retries(Body=html, Bucket=self.configuration_bucket, Key=S3_KEY_ACTIONS_HTML_PAGE, ContentType="text/html") self._logger.info(INF_GENERATING_TEMPLATES, self.configuration_bucket) try: stack = os.getenv(handlers.ENV_STACK_NAME, "") s3_client = get_client_with_retries("s3", ["put_object"], context=self.context) config_template_builder = ActionTemplateBuilder(self.context, service_token_arn="arn:aws:region:account:function:used-for-debug-only", ops_automator_role=self.automator_role_arn, use_ecs=self.use_ecs) role_template_builder = CrossAccountRoleBuilder(self.automator_role_arn, stack) all_actions = [] for action_name in actions.all_actions(): action_properties = actions.get_action_properties(action_name) if not action_properties.get(actions.ACTION_INTERNAL, False): generate_configuration_template(s3_client, config_template_builder, action_name) # Enable to generate a template for every individual action # description = TEMPLATE_DESC_CROSS_ACCOUNT_ACTION.format(action_name, stack, account) # generate_action_cross_account_role_template(s3_client, role_template_builder, action_name, description) all_actions.append(action_name) if len(all_actions) > 0: description = TEMPLATE_DESC_ALL_ACTIONS_PARAMETERS.format(stack, self.account) generate_all_actions_cross_account_role_template_parameterized(s3_client, role_template_builder, all_actions, description) # enable to generate a template with all actions enabled # description = TEMPLATE_DESC_ALL_ACTIONS.format(stack, account) # generate_all_actions_cross_account_role_template(s3_client, role_template_builder, all_actions, description) for action_name in actions.all_actions(): action_properties = actions.get_action_properties(action_name) if action_properties.get(actions.ACTION_EVENTS, None) is not None: generate_forward_events_template(s3_client) break generate_actions_html_page(s3_client) generate_scenario_templates(s3_client) generate_custom_resource_builder(s3_client) except Exception as ex: self._logger.error(ERR_BUILDING_TEMPLATES, str(ex), full_stack())
def get_action(name, context=None, log_this_call=True): """ Gets the details of the specified action :param name: Name of the action, raises an exception if the action does not exist :param context: Lambda context :param log_this_call: switch :return: Details of the specified action. This dictionary can contain the following actions: Constants used below can be found in actions/__init__.py -ACTION_SERVICE: Name of the service of the resources of this action -ACTION_RESOURCES: Name of the resources for this action -ACTION_AGGREGATION: Possible values are: ACTION_AGGREGATION_RESOURCE: resources are not aggregated, execution of the action for each individual resource. ACTION_AGGREGATION_ACCOUNT: resources are aggregated per account, execution of the action for the list of resources in that account ACTION_AGGREGATION_TASK: resources are aggregated per task, single execution of the action for list of all resources in all accounts -ACTION_SELECT_EXPRESSION: Optional JMES path to map/select attributes of and filtering of resources -ACTION_BATCH_SIZE: Optional batch size for aggregated resources. -ACTION_PERMISSIONS: Optional, permissions required for the action -ACTION_MEMORY: Optional memory requirement for lambda function to run action, default is size of the scheduler lambda function -ACTION_CROSS_ACCOUNT: Optional, cross account operations supported by action, default is True -ACTION_EVENT_FILTER: Optional, regex filter which type of source events are supported by the action, default is None (all events) -ACTION_TITLE: Optional, title to be used in UI -ACTION_DESCRIPTION: Optional, description or url to be used in UI -ACTION_AUTHOR: Optional, author of the action -ACTION_VERSION: Optional, implementation version of the action -ACTION_MULTI_REGION: Optional, True if the action can execute in multiple regions (default) -ACTION_INTERNAL: Optional, True if the service can only be used in internal tasks -ACTION_PARAM_STACK_RESOURCES: Optional, cloudformation snippet of resources owen and used by action implementation -ACTION_STACK_RESOURCES_PERMISSIONS: Optional, list of permissions for action stack resources -ACTION_PARAMETERS: Parameters for the action: -PARAM_ALLOWED_VALUES: allowed valued for a parameter (optional) -PARAM_DEFAULT: default value for a parameter (optional) -PARAM_MAX_LEN: max length for a string parameter (optional) -PARAM_MAX_VALUE: max value for a numeric parameter (optional) -PARAM_MIN_LEN: min length for a string parameter (optional) -PARAM_MIN_VALUE: # min value for a numeric parameter (optional) -PARAM_PATTERN: allowed pattern for a string parameter (optional) -PARAM_REQUIRED: true if parameter is required (default=False) -PARAM_TYPE: (Python) type name of a parameter -PARAM_DESCRIBE_PARAMETER: name of a parameter if it must be used as a parameter in the describe method for a resource -PARAM_DESCRIPTION: user readable description for parameter -PARAM_LABEL: label for parameter """ with _get_logger(context=context) as logger: if log_this_call: logger.info("get_action") all_actions = actions.all_actions() if name not in all_actions: raise ValueError(ERR_ACTION_DOES_NOT_EXIST.format(name, ",".join(all_actions))) return safe_json(actions.get_action_properties(name))
def generate_templates(self): """ Generates configuration and cross-account role templates :return: """ def generate_configuration_template(s3, builder, action): configuration_template = S3_CONFIGURATION_TEMPLATE.format(action) self._logger.info(INF_CREATE_ACTION_TEMPLATE, action, configuration_template) template = json.dumps(builder.build_template(action), indent=3) s3.put_object_with_retries(Body=template, Bucket=self.configuration_bucket, Key=configuration_template) def generate_action_cross_account_role_template( s3, builder, action, template_description): role_template = S3_ROLES_TEMPLATE.format(action) self._logger.info(INF_CREATECROSS_ROLE_TEMPLATE, action, role_template) template = json.dumps(builder.build_template( role_actions=[action], description=template_description), indent=3) s3.put_object_with_retries(Body=template, Bucket=self.configuration_bucket, Key=role_template) def generate_all_actions_cross_cross_account_role_template( s3, builder, allactions, template_description): role_template = S3_ROLES_TEMPLATE.format(ALL_ACTIONS_TEMPLATE_NAME) self._logger.info(INF_CREATE_ALL_ACTIONS_CROSS_ROLES_TEMPLAE, role_template) template = json.dumps(builder.build_template( role_actions=allactions, description=template_description), indent=3) s3.put_object_with_retries(Body=template, Bucket=self.configuration_bucket, Key=role_template) self._logger.info(INF_GENERATING_TEMPLATES, self.configuration_bucket) try: account = AwsService.get_aws_account() stack = os.getenv(handlers.ENV_STACK_NAME, "") s3_client = get_client_with_retries("s3", ["put_object"], context=self.context) config_template_builder = ActionTemplateBuilder( self.context, "arn:aws:region:account:function:debug-only") role_template_builder = CrossAccountRoleBuilder( self.scheduler_role_arn) all_actions = [] for action_name in actions.all_actions(): action_properties = actions.get_action_properties(action_name) if not action_properties.get(actions.ACTION_INTERNAL, False): generate_configuration_template(s3_client, config_template_builder, action_name) description = TEMPLATE_DESC_CROSS_ACCOUNT_ACTION.format( action_name, stack, account) generate_action_cross_account_role_template( s3_client, role_template_builder, action_name, description) all_actions.append(action_name) if len(all_actions) > 0: description = TEMPLATE_DESC_ALL_ACTIONS.format(stack, account) generate_all_actions_cross_cross_account_role_template( s3_client, role_template_builder, all_actions, description) except Exception as ex: self._logger.error(ERR_BUILDING_TEMPLATES, str(ex), traceback.format_exc())
#!/usr/bin/env python from server import Server from actions import all_actions import asyncio from domain import Counter, Users import logging import websockets logging.basicConfig() counter = Counter() domain_objects = [counter] users = Users(domian_objects=domain_objects) conn = Server(users=users, actions=all_actions(counter)) for d in domain_objects: d.attach(conn.send_message) start_server = websockets.serve(conn.manage_user_connection, "localhost", 6789) asyncio.get_event_loop().run_until_complete(start_server) asyncio.get_event_loop().run_forever()