def generate_ref_env_var(self, target) -> list: """ Method to define all the env var of a resource based on its own defined output attributes """ if not self.ref_parameter: LOG.error( f"{self.module.res_key}.{self.name}. Default ref_parameter not set. Skipping env_vars" ) return [] env_var_name = ENV_VAR_NAME.sub("", self.name.upper().replace("-", "_")) if self.cfn_resource and self.attributes_outputs and self.ref_parameter: ref_env_var = Environment( Name=env_var_name, Value=Ref(self.attributes_outputs[self.ref_parameter] ["ImportParameter"]), ) ref_param_settings = get_parameter_settings( self, self.ref_parameter) add_parameters(target[0].template, [ref_param_settings[1]]) target[0].stack.Parameters.update( {ref_param_settings[0]: ref_param_settings[2]}) elif self.lookup_properties and self.ref_parameter: ref_env_var = Environment( Name=env_var_name, Value=self.attributes_outputs[self.ref_parameter] ["ImportValue"], ) else: raise ValueError( f"{self.module.res_key}.{self.name} - Unable to set the default env var" ) return [ref_env_var]
def assign_kms_key_to_queue(kms_key, queue, queue_stack, settings): """ Assigns the KMS Key pointer to the queue property :param ecs_composex.kms.kms_stack.KmsKey kms_key: :param queue: :param ecs_composex.sqs.sqs_stack.XStack queue_stack: :param ecs_composex.common.settings.ComposeXSettings settings: :return: """ kms_key_id = kms_key.attributes_outputs[KMS_KEY_ID] if kms_key.cfn_resource: add_parameters(queue_stack.stack_template, [kms_key_id["ImportParameter"]]) setattr( queue.cfn_resource, KEY, Ref(kms_key_id["ImportParameter"]), ) queue_stack.Parameters.update( {kms_key_id["ImportParameter"].title: kms_key_id["ImportValue"]}) elif not kms_key.cfn_resource and kms_key.mappings: add_update_mapping( queue.stack.stack_template, kms_key.module.mapping_key, settings.mappings[kms_key.module.mapping_key], ) setattr(queue.cfn_resource, KEY, kms_key_id["ImportValue"])
def handle_elbv2_dimension_mapping(alarms_stack, dimension, resource, settings): """ Replaces x-elbv2::<name> with a pointer to the LB :param ecs_composex.alarms.alarms_stack.XStack alarms_stack: The alarms stack which has the alarm to modify :param troposphere.cloudwatch.MetricDimension dimension: :param ecs_composex.alarms.alarms_stack.Alarm resource: :param ecs_composex.common.settings.ComposeXSettings settings: :return: The identified LB """ x_elbv2 = settings.compose_content["x-elbv2"] if not isinstance(dimension.Value, str): LOG.warning( f"{alarms_stack.title}{resource.name} - Dimension {dimension.Name} value is {type(dimension.Value)}" ) return lb_name = dimension.Value.split("x-elbv2::")[-1] if lb_name not in x_elbv2.keys(): raise ValueError(f"x-elbv2.{lb_name} is not present in this execution") lb = x_elbv2[lb_name] add_parameters( alarms_stack.stack_template, [lb.attributes_outputs[LB_FULL_NAME]["ImportParameter"]], ) alarms_stack.Parameters.update({ lb.attributes_outputs[LB_FULL_NAME]["ImportParameter"].title: lb.attributes_outputs[LB_FULL_NAME]["ImportValue"] }) dimension.Value = Ref( lb.attributes_outputs[LB_FULL_NAME]["ImportParameter"]) LOG.info( f"x-alarms - Associated {lb.cfn_resource.title} to {alarms_stack.title} - {resource.name}" )
def add_parameters_and_conditions(root_stack): """ Adds parameters and conditions to the root stack :param root_stack: :return: """ add_parameters( root_stack.stack_template, [ dns_params.PUBLIC_DNS_ZONE_ID, dns_params.PUBLIC_DNS_ZONE_NAME, dns_params.PRIVATE_DNS_ZONE_ID, dns_params.PRIVATE_DNS_ZONE_NAME, ], ) root_stack.stack_template.add_condition( dns_conditions.USE_DEFAULT_ZONE_NAME_CON_T, dns_conditions.USE_DEFAULT_ZONE_NAME_CON, ) root_stack.stack_template.add_condition( dns_conditions.CREATE_PRIVATE_NAMESPACE_CON_T, dns_conditions.CREATE_PRIVATE_NAMESPACE_CON, ) root_stack.stack_template.add_condition( dns_conditions.CREATE_PUBLIC_NAMESPACE_CON_T, dns_conditions.CREATE_PUBLIC_NAMESPACE_CON, )
def handle_import_cognito_pool(the_pool, listener_stack, settings): """ Function to map AWS Cognito Pool to attributes :param the_pool: :param listener_stack: :param settings: :return: """ if the_pool.cfn_resource and not the_pool.mappings: pool_id_param = Parameter( f"{the_pool.logical_name}{USERPOOL_ID.title}", Type="String") pool_arn = Parameter(f"{the_pool.logical_name}{USERPOOL_ARN.title}", Type="String") add_parameters(listener_stack.stack_template, [pool_id_param, pool_arn]) listener_stack.Parameters.update({ pool_id_param.title: Ref(the_pool.cfn_resource), pool_arn.title: Ref(pool_arn), }) return Ref(pool_id_param), Ref(pool_arn) elif the_pool.mappings and not the_pool.cfn_resource: add_update_mapping( listener_stack.stack_template, the_pool.module.mapping_key, settings.mappings[the_pool.module.mapping_key], ) return ( FindInMap(COGNITO_MAP, the_pool.logical_name, USERPOOL_ID.title), FindInMap(COGNITO_MAP, the_pool.logical_name, USERPOOL_ARN.return_value), FindInMap(COGNITO_MAP, the_pool.logical_name, USERPOOL_DOMAIN.title), )
def import_resource_into_service_stack( settings: ComposeXSettings, resource, family: ComposeFamily, params_to_add, params_values, ) -> None: """ Function to either add parameters to the services stack or mapping for a given resource :param ecs_composex.common.settings.ComposeXSettings settings: :param ecs_composex.common.compose_resources.ServicesXResource resource: The resource :param ecs_composex.ecs.ecs_family.ComposeFamily family: :param list[ecs_composex.common.cfn_params.Parameter] params_to_add: :param dict params_values: """ if resource.cfn_resource: add_parameters(family.template, params_to_add) family.stack.Parameters.update(params_values) elif resource.mappings: add_update_mapping( family.template, resource.module.mapping_key, settings.mappings[resource.module.mapping_key], )
def add_parameter_to_family_stack( self, family, settings: ComposeXSettings, parameter: Union[str, Parameter]) -> dict: if self.stack == family.stack: LOG.warning( "Cannot add parameter to resource", f"{self.name}", "because it is in the same stack as family", "{family.name}", ) return self if (isinstance(parameter, str) and parameter in self.property_to_parameter_mapping.keys()): the_parameter = self.property_to_parameter_mapping[parameter] elif (isinstance(parameter, Parameter) and parameter in self.property_to_parameter_mapping.values()): the_parameter = parameter else: raise TypeError("parameter must be one of", str, Parameter, "got", type(parameter)) if self.mappings and self.lookup: add_update_mapping( family.template, self.module.mapping_key, settings.mappings[self.module.mapping_key], ) return self.attributes_outputs[the_parameter] param_id = self.attributes_outputs[the_parameter] add_parameters(family.template, [param_id["ImportParameter"]]) family.stack.Parameters.update( {param_id["ImportParameter"].title: param_id["ImportValue"]}) return param_id
def family(self, family: ComposeFamily): self.__family = family if family.template: add_parameters(family.template, [self.image.image_param]) if (family.stack and isinstance(self.image, ServiceImage) and isinstance(self.image.image, str)): family.stack.Parameters.update( {self.image.image_param.title: self.image.image_uri})
def s3_to_firehose( resource: Bucket, dest_resource: DeliveryStream, dest_resource_stack, settings: ComposeXSettings, ) -> None: """ Updates :param Bucket resource: :param DeliveryStream dest_resource: :param dest_resource_stack: :param settings: :return: """ if not dest_resource.cfn_resource: LOG.error( f"{dest_resource.module.res_key}.{dest_resource.name} - Not a new resource" ) for prop_path, bucket_param in FIREHOSE_PROPERTIES.items(): prop_attr = get_dest_resource_nested_property( prop_path, dest_resource.cfn_resource) if skip_if(resource, prop_attr): continue bucket_id = resource.attributes_outputs[bucket_param] if resource.cfn_resource: add_parameters(dest_resource_stack.stack_template, [bucket_id["ImportParameter"]]) setattr( prop_attr[0], prop_attr[1], Ref(bucket_id["ImportParameter"]), ) dest_resource.stack.Parameters.update( {bucket_id["ImportParameter"].title: bucket_id["ImportValue"]}) arn_pointer = Ref(bucket_id["ImportParameter"]) elif not resource.cfn_resource and resource.mappings: add_update_mapping( dest_resource.stack.stack_template, resource.module.mapping_key, settings.mappings[resource.module.mapping_key], ) setattr(prop_attr[0], prop_attr[1], bucket_id["ImportValue"]) arn_pointer = bucket_id["ImportValue"] else: raise ValueError( resource.module.mapping_key, resource.name, "Unable to determine if new or lookup", ) map_x_resource_perms_to_resource( dest_resource, arn_value=arn_pointer, access_definition="s3destination", access_subkey="kinesis_firehose", resource_policies=get_access_types(resource.module.mod_key), resource_mapping_key=resource.module.mapping_key, ) dest_resource.ensure_iam_policies_dependencies()
def add_efs_definition_to_target_family(new_efs, target): add_parameters( target[0].template, [new_efs.attributes_outputs[FS_ARN]["ImportParameter"]], ) target[0].stack.Parameters.update({ new_efs.attributes_outputs[FS_ARN]["ImportParameter"].title: new_efs.attributes_outputs[FS_ARN]["ImportValue"] })
def add_service_actions(alarm, alarms_stack, target, scaling_in_policy, scaling_out_policy): """ Function to update the alarm properties with OKActions and AlarmActions :param ecs_composex.alarms.alarms_stack.Alarm alarm: :param ecs_composex.common.stacks.ComposeXStack alarms_stack: :param tuple target: :param scaling_in_policy: :param scaling_out_policy: """ setattr( alarm, "Threshold", float(scaling_out_policy.StepScalingPolicyConfiguration. StepAdjustments[0].MetricIntervalLowerBound), ) if not alarm.cfn_resource: raise AttributeError( f"Alarm {alarm.logical_name} has no CFN object associated") service_scaling_in_policy_param = Parameter( f"{target[0].logical_name}ScaleInPolicy", Type="String") service_scaling_out_policy_param = Parameter( f"{target[0].logical_name}ScaleOutPolicy", Type="String") add_parameters( alarms_stack.stack_template, [service_scaling_in_policy_param, service_scaling_out_policy_param], ) add_outputs( target[0].template, [ Output( f"{target[0].logical_name}ScaleInPolicy", Value=Ref(scaling_in_policy), ), Output( f"{target[0].logical_name}ScaleOutPolicy", Value=Ref(scaling_out_policy), ), ], ) alarms_stack.Parameters.update({ service_scaling_in_policy_param.title: GetAtt( target[0].logical_name, f"Outputs.{target[0].logical_name}ScaleInPolicy", ), service_scaling_out_policy_param.title: GetAtt( target[0].logical_name, f"Outputs.{target[0].logical_name}ScaleOutPolicy", ), }) actions = get_alarm_actions(alarm) actions[0].append(Ref(service_scaling_in_policy_param)) actions[1].append(Ref(service_scaling_out_policy_param))
def create_record(name, route53_zone, route53_stack, target_elbv2, elbv2_stack) -> None: """ Create a new RecordResource with the given DNS Name pointing to the ELB :param str name: :param ecs_composex.route53.route53_zone.HostedZone route53_zone: :param ecs_composex.route53.route53_stack.XStack route53_stack: :param ecs_composex.elbv2.elbv2_stack.Elbv2 target_elbv2: :param ComposeXStack elbv2_stack: """ if not target_elbv2.attributes_outputs: target_elbv2.init_outputs() target_elbv2.generate_outputs() add_outputs(elbv2_stack.stack_template, target_elbv2.outputs) lb_zone_id = target_elbv2.attributes_outputs[LB_DNS_ZONE_ID] lb_dns_name = target_elbv2.attributes_outputs[LB_DNS_NAME] add_parameters( route53_stack.stack_template, [lb_zone_id["ImportParameter"], lb_dns_name["ImportParameter"]], ) route53_stack.Parameters.update({ lb_zone_id["ImportParameter"].title: lb_zone_id["ImportValue"], lb_dns_name["ImportParameter"].title: lb_dns_name["ImportValue"], }) elbv2_alias = AliasTarget( HostedZoneId=Ref(lb_zone_id["ImportParameter"]), DNSName=Ref(lb_dns_name["ImportParameter"]), ) record_props = { "AliasTarget": elbv2_alias, "Region": Ref(AWS_REGION), "Type": "A", "Name": name, } if not keyisset("SetIdentifier", record_props): record_props["SetIdentifier"] = Ref(AWS_STACK_NAME) if route53_zone.cfn_resource: zone_id_attribute = GetAtt(route53_zone.cfn_resource, PUBLIC_DNS_ZONE_ID.return_value) record_props["HostedZoneId"] = zone_id_attribute elif route53_zone.mappings: zone_id_attribute = route53_zone.attributes_outputs[PUBLIC_DNS_ZONE_ID] record_props["HostedZoneId"] = zone_id_attribute["ImportValue"] cfn_resource = RecordSetType( f"elbv2{target_elbv2.logical_name}Route53{record_props['Type']}{NONALPHANUM.sub('', record_props['Name'])}"[: 128], **record_props, ) if cfn_resource.title not in route53_stack.stack_template.resources: route53_stack.stack_template.add_resource(cfn_resource) if elbv2_stack.title not in route53_stack.DependsOn: route53_stack.DependsOn.append(elbv2_stack.title)
def handle_elbv2_target_group_dimensions(alarms_stack, dimension, resource, settings): """ Matches up the x-elbv2::<lb>::<service>::port provided input to the new resource for alarms :param ecs_composex.alarms.alarms_stack.XStack alarms_stack: The alarms stack which has the alarm to modify :param troposphere.cloudwatch.MetricDimension dimension: :param ecs_composex.alarms.alarms_stack.Alarm resource: :param ecs_composex.common.settings.ComposeXSettings settings: :return: """ if not isinstance(dimension.Value, str): LOG.warning( f"{alarms_stack.title}{resource.name} - Dimension {dimension.Name} value is {type(dimension.Value)}" ) return parts = validate_tgt_input(dimension, settings) for family in settings.families.values(): if family.name == parts.group("svc"): break else: raise KeyError( "alarms. unable to find services family", parts.group("svc"), "service families available", [_.name for _ in settings.families.values()], ) port = int(parts.group("port")) the_tgt = None the_lb = get_target_lb(parts, dimension, resource, settings) for target_group in family.target_groups: if target_group.elbv2 == the_lb and target_group.Port == port: the_tgt = target_group if the_tgt is None: raise ValueError( f"Family {family.logical_name} has not target group associated with", the_lb.name, "Associated LBs", [tgt.elbv2.name for tgt in family.target_groups], ) else: add_parameters( alarms_stack.stack_template, [the_tgt.attributes_outputs[TGT_FULL_NAME]["ImportParameter"]], ) alarms_stack.Parameters.update({ the_tgt.attributes_outputs[TGT_FULL_NAME]["ImportParameter"].title: the_tgt.attributes_outputs[TGT_FULL_NAME]["ImportValue"] }) dimension.Value = Ref( the_tgt.attributes_outputs[TGT_FULL_NAME]["ImportParameter"]) LOG.info( f"x-alarms - Associated {the_tgt.title} to {alarms_stack.title} - {resource.name}" )
def update_stack_with_resource_settings( property_stack: ComposeXStack, the_resource: Certificate, the_property, property_name: str, settings: ComposeXSettings, ) -> None: """ Assigns the CFN pointer to the value to replace. If it is a new certificate, it will add the parameter to get the cert ARN and set the parameter stack value If it is a Lookup certificate, it will add the mapping to the stack and set FindInMap to the certificate ARN :param ComposeXStack property_stack: :param Certificate the_resource: :param the_property: :param property_name: :param ecs_composex.common.settings.ComposeXSettings settings: """ if the_resource.cfn_resource: add_parameters( property_stack.stack_template, [the_resource.attributes_outputs[CERT_ARN]["ImportParameter"]], ) property_stack.Parameters.update( { the_resource.attributes_outputs[CERT_ARN][ "ImportParameter" ].title: the_resource.attributes_outputs[CERT_ARN]["ImportValue"] } ) setattr( the_property, property_name, Ref(the_resource.attributes_outputs[CERT_ARN]["ImportParameter"]), ) elif the_resource.mappings: if keyisset( the_resource.module.mapping_key, property_stack.stack_template.mappings ): property_stack.stack_template.mappings[the_resource.module.mapping_key][ the_resource.logical_name ].update(the_resource.mappings) else: add_update_mapping( property_stack.stack_template, the_resource.module.mapping_key, settings.mappings[the_resource.module.mapping_key], ) setattr( the_property, property_name, the_resource.attributes_outputs[CERT_ARN]["ImportValue"], )
def process_return_values( namespace: PrivateNamespace, resource, return_values: dict, instance_props: dict, settings, ): """ Processes the ReturnValues attributes to assign to an instance. :param namespace: :param ecs_composex.compose.x_resources.XResource resource: :param dict return_values: :param dict instance_props: :param ecs_composex.common.settings.ComposeXSettings settings: """ for key, value in return_values.items(): for attribute_param in resource.attributes_outputs.keys(): if attribute_param.title == key or ( attribute_param.return_value and attribute_param.return_value == key ): break else: raise KeyError( f"{resource.module.res_key}.{resource.name}" " - ReturnValue {key} not found. Available", [p.title for p in resource.attributes_outputs.keys()], ) attribute_pointer = resource.attributes_outputs[attribute_param] if resource.cfn_resource: add_parameters( namespace.stack.stack_template, [attribute_pointer["ImportParameter"]] ) namespace.stack.Parameters.update( { attribute_pointer["ImportParameter"].title: attribute_pointer[ "ImportValue" ] } ) instance_props["InstanceAttributes"][key] = Ref( attribute_pointer["ImportParameter"] ) else: add_update_mapping( namespace.stack.stack_template, resource.module.mapping_key, settings.mappings[resource.module.mapping_key], ) instance_props["InstanceAttributes"][value] = attribute_pointer[ "ImportValue" ]
def set_for_new_kms_key(prop_attr, resource_id, dest_resource, dest_resource_stack) -> AWSHelperFn: add_parameters(dest_resource_stack.stack_template, [resource_id["ImportParameter"]]) setattr( prop_attr[0], prop_attr[1], Ref(resource_id["ImportParameter"]), ) setattr(prop_attr[0], "KeyType", "CUSTOMER_MANAGED_CMK") dest_resource.stack.Parameters.update( {resource_id["ImportParameter"].title: resource_id["ImportValue"]}) return Ref(resource_id["ImportParameter"])
def get_s3_bucket_arn_from_resource(db_stack, resource): param = resource.attributes_outputs[S3_BUCKET_ARN]["ImportParameter"] add_parameters(db_stack.stack_template, [param]) if db_stack.parent_stack: add_parameters(db_stack.parent_stack.stack_template, [param]) db_stack.parent_stack.Parameters.update({ param.title: GetAtt( "s3", f"Outputs.{resource.logical_name}{S3_BUCKET_ARN.title}", ) }) db_stack.Parameters.update({param.title: Ref(param.title)}) return Sub(f"${{{param.title}}}/*")
def set_vpc_params_from_vpc_stack_import(self, vpc_stack): """ Method to set the stack parameters when we are not creating a VPC. """ add_parameters(self.stack_template, vpc_stack.vpc_resource.subnets_parameters) add_parameters(self.stack_template, [VPC_ID]) self.Parameters.update( {VPC_ID.title: FindInMap("Network", VPC_ID.title, VPC_ID.title)}) for subnet_param in vpc_stack.vpc_resource.subnets_parameters: self.Parameters.update({ subnet_param.title: Join(",", FindInMap("Network", subnet_param.title, "Ids")) })
def process_dns_config( namespace: PrivateNamespace, resource: NetworkXResource | DatabaseXResource, dns_settings: dict, settings: ComposeXSettings, service: Service, instance: Instance, ) -> None: """ Process the DnsSettings of the x-cloudmap configuration """ hostname = ( dns_settings["Hostname"] if keyisset("Hostname", dns_settings) else resource.logical_name ) if namespace.zone_name not in hostname: hostname = f"{hostname}.{namespace.zone_name}" record = DnsRecord(Type="CNAME", TTL="15") config = DnsConfig(DnsRecords=[record], NamespaceId=NoValue) setattr(service, "DnsConfig", config) setattr(service, "Name", hostname) if not hasattr(resource, "db_cluster_endpoint_param"): return attribute_pointer = resource.attributes_outputs[resource.db_cluster_endpoint_param] if resource.cfn_resource: add_parameters( namespace.stack.stack_template, [attribute_pointer["ImportParameter"]] ) namespace.stack.Parameters.update( { attribute_pointer["ImportParameter"].title: attribute_pointer[ "ImportValue" ] } ) instance.InstanceAttributes.update( {"AWS_INSTANCE_CNAME": Ref(attribute_pointer["ImportParameter"])} ) else: add_update_mapping( namespace.stack.stack_template, resource.module.mapping_key, settings.mappings[resource.module.mapping_key], ) instance.InstanceAttributes.update( {"AWS_INSTANCE_CNAME": attribute_pointer["ImportValue"]} )
def expand_family_with_efs_volumes(efs_root_stack_title, new_efs, settings): """ Function to add the EFS Volume definition to the task definition for the service to use. :param efs_root_stack_title: Root stack title for EFS :param new_efs: :param ecs_composex.common.settings.ComposeXSettings settings: :return: """ fs_id_parameter = new_efs.attributes_outputs[FS_ID]["ImportParameter"] fs_id_getatt = new_efs.attributes_outputs[FS_ID]["ImportValue"] for target in new_efs.families_targets: if target[0].service_compute.launch_type == "EXTERNAL": LOG.warning( f"x-efs - {target[0].name} - When using EXTERNAL Launch Type, networking settings cannot be set." ) return access_points = [] target[0].stack.Parameters.update( {fs_id_parameter.title: fs_id_getatt}) add_parameters(target[0].template, [fs_id_parameter]) task_definition = target[0].template.resources[TASK_T] efs_config_kwargs = {"FilesystemId": Ref(fs_id_parameter)} if (new_efs.parameters and keyisset("EnforceIamAuth", new_efs.parameters) or [service.user for service in target[2]]): add_efs_definition_to_target_family(new_efs, target) efs_access_point = target[0].template.add_resource( AccessPoint( f"{new_efs.logical_name}{target[0].logical_name}EfsAccessPoint", FileSystemId=Ref(fs_id_parameter), )) access_points.append(efs_access_point) efs_config_kwargs.update({ "AuthorizationConfig": AuthorizationConfig(AccessPointId=Ref(efs_access_point), IAM="ENABLED"), "TransitEncryption": "ENABLED", }) efs_volume_definition = Volume( EFSVolumeConfiguration=EFSVolumeConfiguration(**efs_config_kwargs), Name=new_efs.volume.volume_name, ) volumes = get_volumes(task_definition) volumes.append(efs_volume_definition) override_efs_settings(new_efs, target, fs_id_parameter, access_points, volumes) add_task_iam_access_to_access_point(target[0], access_points, new_efs)
def __init__(self, name: str, settings: ComposeXSettings, **kwargs): stack_template = build_template("Root stack for IAM Roles") add_parameters(stack_template, [CLUSTER_NAME]) super().__init__(name, stack_template, **kwargs) exec_role_managed_policy = add_ecs_execution_role_managed_policy(stack_template) self.Parameters.update( {CLUSTER_NAME.title: settings.ecs_cluster.cluster_identifier} ) new_roles = import_family_roles(settings, exec_role_managed_policy) for role in new_roles: self.stack_template.add_resource(role.cfn_resource) role.stack = self if not role.attributes_outputs: role.generate_outputs() add_outputs(stack_template, role.outputs)
def add_to_family(self, family: ComposeFamily, is_dependency: bool = False) -> None: """ Adds the container as a sidecar to the family in order to fulfil a specific purpose for an AWS Feature, here, add xray-daemon for dynamic tracing. :param ecs_composex.ecs.ecs_family.ComposeFamily family: :param bool is_dependency: Whether the family services depend on sidecar or not. """ self.family = family family.add_managed_sidecar(self) add_parameters(family.template, [FLUENT_BIT_IMAGE_PARAMETER]) self.image_param = FLUENT_BIT_IMAGE_PARAMETER self.set_as_dependency_to_family_services(is_dependency)
def set_vpc_parameters_from_vpc_stack(self, vpc_stack, *parameters): """ When a new VPC is created (vpc comes from nested stack), adds the subnets parameters and updates the stack parameters in the root stack. :param vpc_stack: :param list parameters: """ if isinstance(vpc_stack, ComposeXStack) or issubclass( type(vpc_stack), ComposeXStack): vpc = vpc_stack.title elif isinstance(vpc_stack, str): vpc = vpc_stack else: raise TypeError( "vpc_stack must be of type", [ComposeXStack, str], "got", type(vpc_stack), ) default_parameters = [ VPC_ID, PUBLIC_SUBNETS, STORAGE_SUBNETS, APP_SUBNETS, ] if not parameters: add_parameters(self.stack_template, default_parameters) self.Parameters.update({ VPC_ID_T: GetAtt(vpc_stack, f"Outputs.{VPC_ID_T}"), PUBLIC_SUBNETS_T: GetAtt(vpc_stack, f"Outputs.{PUBLIC_SUBNETS_T}"), APP_SUBNETS_T: GetAtt(vpc_stack, f"Outputs.{APP_SUBNETS_T}"), STORAGE_SUBNETS_T: GetAtt(vpc_stack, f"Outputs.{STORAGE_SUBNETS_T}"), }) else: for parameter in parameters: self.Parameters.update( {parameter: GetAtt(vpc_stack, f"Outputs.{parameter}")}) if not hasattr(self, "DependsOn"): self.DependsOn = [vpc] elif hasattr(self, "DependsOn") and vpc not in getattr( self, "DependsOn"): self.DependsOn.append(vpc)
def set_update_container_env_var(self, target: tuple, parameter, env_var_name: str) -> list: """ Function that will set or update the value of a given env var from Return value of a resource. :param tuple target: :param parameter: """ if isinstance(parameter, str): try: attr_parameter = self.property_to_parameter_mapping[parameter] except KeyError: LOG.error( f"{self.module.res_key}.{self.name} - No return value {parameter} available." ) return [] elif isinstance(parameter, Parameter): attr_parameter = parameter else: raise TypeError("parameter is", type(parameter), "must be one of", [str, Parameter]) env_vars = [] params_to_add = [] attr_id = self.attributes_outputs[attr_parameter] if self.cfn_resource: env_vars.append( Environment( Name=env_var_name, Value=Ref(attr_id["ImportParameter"]), )) params_to_add.append(attr_parameter) elif self.lookup_properties: env_vars.append( Environment( Name=env_var_name, Value=attr_id["ImportValue"], )) if params_to_add: params_values = {} settings = [ get_parameter_settings(self, param) for param in params_to_add ] resource_params_to_add = [] for setting in settings: resource_params_to_add.append(setting[1]) params_values[setting[0]] = setting[2] add_parameters(target[0].template, resource_params_to_add) target[0].stack.Parameters.update(params_values) return env_vars
def new_dns_zone(route53_zone, acm_stack, validation_option): """ Update the HostedZoneId property when using a new Route53 zone :param route53_zone: :param acm_stack: :param troposphere.certificatemanager.DomainValidationOption validation_option: """ zone_id_attribute = route53_zone.attributes_outputs[PUBLIC_DNS_ZONE_ID] add_parameters(acm_stack.stack_template, [zone_id_attribute["ImportParameter"]]) acm_stack.Parameters.update( {zone_id_attribute["ImportParameter"].title: zone_id_attribute["ImportValue"]} ) setattr( validation_option, "HostedZoneId", Ref(zone_id_attribute["ImportParameter"]) )
def add_containers_images_cfn_parameters(self): """ Adds parameters to the stack and set values for each service/container in the family definition """ if not self.template or not self.stack: return images_parameters = [] for service in chain(self.managed_sidecars, self.ordered_services): if service.image.image_param.title not in self.stack.Parameters: if isinstance(service.image.image, str): self.stack.Parameters.update( {service.image.image_param.title: service.image.image} ) elif isinstance(service.image.image, Ref): LOG.debug(f"{service.name} image is Parameter already.") images_parameters.append(service.image.image_param) add_parameters(self.template, images_parameters)
def set_ecs_cluster_logging_cw_access( settings: ComposeXSettings, policy, role_stack: ComposeXStack ) -> None: """ Based on ECS Cluster settings / configurations, grant permissions to CW Log defined to log ECS Execute command feature :param ecs_composex.common.settings.ComposeXSettings settings: :param policy: :param ecs_composex.common.stacks.ComposeXStack role_stack: """ if settings.ecs_cluster.log_group and settings.ecs_cluster.log_group != NoValue: parameter = Parameter("EcsExecuteLoggingGroup", Type="String") add_parameters(role_stack.stack_template, [parameter]) if isinstance(settings.ecs_cluster.log_group, FindInMap): role_stack.Parameters.update( {parameter.title: settings.ecs_cluster.log_group} ) arn_value = Sub( f"arn:${{{AWS_PARTITION}}}:logs:${{{AWS_REGION}}}:" f"${{{AWS_ACCOUNT_ID}}}:${{{parameter.title}}}:*" ) else: role_stack.Parameters.update( {parameter.title: GetAtt(settings.ecs_cluster.log_group, "Arn")} ) arn_value = Ref(parameter) policy.PolicyDocument["Statement"].append( { "Sid": "AllowDescribingAllCWLogGroupsForSSMClient", "Action": ["logs:DescribeLogGroups"], "Resource": ["*"], "Effect": "Allow", } ) policy.PolicyDocument["Statement"].append( { "Action": [ "logs:CreateLogStream", "logs:DescribeLogStreams", "logs:PutLogEvents", ], "Resource": [arn_value], "Effect": "Allow", } )
def extend_service_stack(self): """ Method to expand the service template with the AppMesh virtual node """ sd_service_name = f"{self.stack.title}DiscoveryService" sd_service = self.stack.stack_template.resources[sd_service_name] self.node = appmesh.VirtualNode( f"{self.stack.title}VirtualNode", MeshName=Ref(appmesh_params.MESH_NAME), MeshOwner=Ref(appmesh_params.MESH_OWNER_ID), VirtualNodeName=GetAtt(sd_service, "Name"), Spec=appmesh.VirtualNodeSpec( ServiceDiscovery=appmesh.ServiceDiscovery( AWSCloudMap=appmesh.AwsCloudMapServiceDiscovery( NamespaceName=Ref(PRIVATE_DNS_ZONE_NAME), ServiceName=GetAtt(sd_service, "Name"), ) ), Listeners=[ appmesh.Listener(PortMapping=port_mapping) for port_mapping in self.port_mappings ], ), Metadata=metadata, ) self.stack.stack_template.add_resource(self.node) add_parameters( self.stack.stack_template, [appmesh_params.MESH_OWNER_ID, appmesh_params.MESH_NAME], ) self.stack.Parameters.update( { appmesh_params.MESH_NAME.title: Ref(appmesh_params.MESH_NAME), appmesh_params.MESH_OWNER_ID.title: Ref(appmesh_params.MESH_OWNER_ID), } ) appmesh_conditions.add_appmesh_conditions(self.stack.stack_template) self.stack.stack_template.add_output( ComposeXOutput( self.node, [("VirtualNode", "", GetAtt(self.node, "VirtualNodeName"))], duplicate_attr=True, export=False, ).outputs )
def add_compose_families(settings: ComposeXSettings) -> None: """ Using existing ComposeFamily in settings, creates the ServiceStack and template :param ecs_composex.common.settings.ComposeXSettings settings: """ for family_name, family in settings.families.items(): family.init_family() initialize_family_services(settings, family) add_parameters( family.template, [ family.iam_manager.task_role.arn_param, family.iam_manager.task_role.name_param, family.iam_manager.exec_role.arn_param, family.iam_manager.exec_role.name_param, ], ) family.stack.Parameters.update({ ecs_params.CLUSTER_NAME.title: settings.ecs_cluster.cluster_identifier, ecs_params.FARGATE_VERSION.title: FindInMap("ComposeXDefaults", "ECS", "PlatformVersion"), family.iam_manager.task_role.arn_param.title: family.iam_manager.task_role.output_arn, family.iam_manager.task_role.name_param.title: family.iam_manager.task_role.output_name, family.iam_manager.exec_role.arn_param.title: family.iam_manager.exec_role.output_arn, family.iam_manager.exec_role.name_param.title: family.iam_manager.exec_role.output_name, ecs_params.SERVICE_HOSTNAME.title: family.family_hostname, }) family.template.metadata.update(metadata) settings.root_stack.stack_template.add_resource(family.stack) family.validate_compute_configuration_for_task(settings) families_stacks = [ family for family in settings.root_stack.stack_template.resources if (family in settings.families and isinstance(settings.families[family].stack, ServiceStack)) ] handle_families_dependencies(settings, families_stacks)
def apply_vpc_settings(x_settings, root_stack): """ :param x_settings: :param root_stack: :return: """ add_parameters( root_stack.stack_template, [VPC_ID, APP_SUBNETS, STORAGE_SUBNETS, PUBLIC_SUBNETS], ) settings_params = { VPC_ID.title: x_settings[VPC_ID.title], APP_SUBNETS.title: x_settings[APP_SUBNETS.title], STORAGE_SUBNETS.title: x_settings[STORAGE_SUBNETS.title], PUBLIC_SUBNETS.title: x_settings[PUBLIC_SUBNETS.title], } root_stack.Parameters.update(settings_params)