Example #1
0
 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]
Example #2
0
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"])
Example #3
0
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}"
    )
Example #4
0
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,
    )
Example #5
0
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),
        )
Example #6
0
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],
        )
Example #7
0
    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
Example #8
0
 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})
Example #9
0
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()
Example #10
0
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"]
    })
Example #11
0
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)
Example #13
0
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}"
        )
Example #14
0
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"
            ]
Example #16
0
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}}}/*")
Example #18
0
 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"]}
        )
Example #20
0
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)
Example #21
0
 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)
Example #23
0
    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)
Example #24
0
 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
Example #25
0
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"])
    )
Example #26
0
 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",
            }
        )
Example #28
0
 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
     )
Example #29
0
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)
Example #30
0
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)