Example #1
0
 def __init__(self, title, template, *args, **kwargs):
     self.props['Port'] = (str, True)
     self.props['Certificates'] = (list, True)
     self.props['VpcId'] = (str, True)
     super().__init__(title, template, *args, **kwargs)
     target_group = TargetGroup(title + 'dummy',
                                VpcId=self.properties['VpcId'],
                                Port='80',
                                Protocol='HTTP')
     template.add_resource(target_group)
     template.add_resource(
         Listener(title + "httpslistener",
                  LoadBalancerArn=Ref(self),
                  Port=self.properties['Port'],
                  Protocol='HTTPS',
                  Certificates=self.properties['Certificates'],
                  DefaultActions=[
                      Action(Type='forward',
                             TargetGroupArn=Ref(target_group))
                  ]))
     self.props.pop('Port')
     self.properties.pop('Port')
     self.props.pop('Certificates')
     self.properties.pop('Certificates')
     self.props.pop('VpcId')
     self.properties.pop('VpcId')
Example #2
0
def __create_load_balancer():
    template = Template()
    load_balancer = template.add_resource(resource=LoadBalancer(
        title='SampleFargateLoadBalancer',
        Name='sample-fargate-load-balancer',
        Subnets=[
            ImportValue(CommonResource.ExportName.PUBLIC_SUBNET_A_ID.value),
            ImportValue(CommonResource.ExportName.PUBLIC_SUBNET_B_ID.value)
        ],
        SecurityGroups=[ImportValue(ExportName.ALB_SECURITY_GROUP.value)],
        Scheme='internet-facing'))

    target_group = template.add_resource(resource=TargetGroup(
        title='SampleFargateTargetGroup',
        Port=80,
        Protocol='HTTP',
        TargetType='ip',
        VpcId=ImportValue(CommonResource.ExportName.VPC_ID.value)))
    template.add_output(output=Output(title=target_group.title,
                                      Value=Ref(target_group),
                                      Export=Export(
                                          name=ExportName.TARGET_GROUP.value)))

    template.add_resource(resource=Listener(
        title='SampleFargateListener',
        DefaultActions=[
            Action(Type='forward', TargetGroupArn=Ref(target_group))
        ],
        LoadBalancerArn=Ref(load_balancer),
        Port=80,
        Protocol='HTTP'))

    output_template_file(template, 'alb.yml')

    return target_group
Example #3
0
 def test_instance_targettype_requires_properties(self):
     with self.assertRaises(ValueError) as valueError:
         TargetGroup("targetGroup", TargetType="instance").to_dict()
     self.assertEqual(
         'TargetType of "instance" in "TargetGroup" requires ' +
         "definitions of 'Port', 'Protocol', 'VpcId'",
         str(valueError.exception),
     )
Example #4
0
 def test_no_targettype_requires_properties(self):
     with self.assertRaises(ValueError) as valueError:
         TargetGroup("targetGroup").to_dict()
     self.assertEqual(
         'Omitting TargetType in "TargetGroup" ' +
         "requires definitions of 'Port', 'Protocol', 'VpcId'",
         str(valueError.exception),
     )
Example #5
0
def create_alb_template():
    template = Template()

    vpc = template.add_parameter(
        parameter=Parameter(title='Vpc', Type='String'))
    subnet_a = template.add_parameter(
        parameter=Parameter(title='SubnetA', Type='String'))
    subnet_b = template.add_parameter(
        parameter=Parameter(title='SubnetB', Type='String'))
    ec2_instance = template.add_parameter(
        parameter=Parameter(title='Ec2Instance', Type='String'))

    security_group = template.add_resource(
        resource=SecurityGroup(title='SampleSecurityGroup',
                               GroupDescription='sample-security-group',
                               SecurityGroupIngress=[{
                                   'IpProtocol': 'tcp',
                                   'FromPort': 80,
                                   'ToPort': 80,
                                   'CidrIp': '0.0.0.0/0'
                               }],
                               VpcId=Ref(vpc)))

    load_balancer = template.add_resource(resource=LoadBalancer(
        title='SampleLoadBalancer',
        Name='sample-alb',
        Subnets=[Ref(subnet_a), Ref(subnet_b)],
        SecurityGroups=[Ref(security_group)],
    ))

    target_group = template.add_resource(resource=TargetGroup(
        title='SampleTargetGroup',
        Targets=[TargetDescription(
            Id=Ref(ec2_instance),
            Port=80,
        )],
        VpcId=Ref(vpc),
        Name='sample-target-group',
        Port=80,
        Protocol='HTTP',
    ))

    template.add_resource(resource=Listener(
        title='SampleListener',
        DefaultActions=[
            Action(TargetGroupArn=Ref(target_group), Type='forward')
        ],
        LoadBalancerArn=Ref(load_balancer),
        Port=80,
        Protocol='HTTP',
    ))

    with open('./alb.yml', mode='w') as file:
        file.write(template.to_yaml())
 def test_ip_targettype_requires_properties(self):
     with self.assertRaises(ValueError) as valueError:
         TargetGroup(
             "targetGroup",
             TargetType="ip"
         ).to_dict()
     self.assertEqual(
         "TargetType of \"ip\" in \"TargetGroup\" " +
         "requires definitions of 'Port', 'Protocol', 'VpcId'",
         str(valueError.exception)
     )
Example #7
0
 def test_invalid_targettype_is_rejected(self):
     with self.assertRaises(ValueError) as valueError:
         TargetGroup(
             "targetGroup",
             TargetType="invalid",
             Port=433,
             Protocol="HTTPS",
             VpcId="unknown",
         ).to_dict()
     self.assertEqual(
         'TargetGroup.TargetType must be one of: "instance, ip, lambda"',
         str(valueError.exception),
     )
Example #8
0
 def test_lambda_targettype_rejects_properties(self):
     with self.assertRaises(ValueError) as valueError:
         TargetGroup(
             "targetGroup",
             TargetType="lambda",
             Port=433,
             Protocol="HTTPS",
             VpcId="unknown",
         ).to_dict()
     self.assertEqual(
         'TargetType of "lambda" in "TargetGroup" must not contain ' +
         "definitions of 'Port', 'Protocol', 'VpcId'",
         str(valueError.exception),
     )
Example #9
0
    def add_target_group(self, port, lb):
        """
        Method to generate the TargetGroups based on the ports of the service.

        :param port: the port to add the targetgroup for
        :type port: int
        :param lb: the loadbalancer the targetgroup will be bound to
        :type lb: troposphere.elasticloadbalancingv2.LoadBalancer
        :return: target group
        :rtype: troposphere.elasticloadbalancingv2.TargetGroup
        """
        suffix = "Private"
        if self.config.is_public:
            suffix = "Public"
        tgt = TargetGroup(
            f"{self.config.lb_type.title()}{suffix}TargetGroupPort{port}".
            strip(),
            template=self.template,
            DependsOn=[lb],
            VpcId=Ref(VPC_ID),
            Port=port,
            Protocol="TCP" if self.config.lb_type == "network" else "HTTP",
            TargetType="ip",
            HealthCheckIntervalSeconds=10,
            HealthyThresholdCount=2,
            UnhealthyThresholdCount=2,
            TargetGroupAttributes=[
                TargetGroupAttribute(
                    Key="deregistration_delay.timeout_seconds", Value="10")
            ],
            Tags=Tags({
                "Name": Sub(f"${{{SERVICE_NAME_T}}}-{port}"),
                "StackName": Ref(AWS_STACK_NAME),
                "StackId": Ref("AWS::StackId"),
                "MicroserviceName": Ref(SERVICE_NAME_T),
            }),
        )
        return tgt
 def add_alb_target_group(self):
     '''
     Add load balancer target group to template
     '''
     self.cfn_template.add_resource(TargetGroup(
         title=constants.ALB_TG,
         HealthCheckIntervalSeconds=int('5'),
         HealthCheckPath='/health',
         HealthCheckProtocol='HTTP',
         HealthCheckTimeoutSeconds=int('3'),
         HealthyThresholdCount=int('2'),
         Matcher=Matcher(HttpCode='200'),
         Port=int('8228'),
         Protocol='HTTP',
         UnhealthyThresholdCount=int('5'),
         TargetGroupAttributes=[
             TargetGroupAttribute(
                 Key='deregistration_delay.timeout_seconds',
                 Value='20'
             )
         ],
         VpcId=ImportValue(Sub('${Environment}-${VpcId}'))
     ))
     return self.cfn_template
    def __init__(self,
                 prefix: str,
                 lb_security_groups: List[SecurityGroup],
                 subnets: List[Subnet],
                 vpc: VPC,
                 desired_domain_name: str,
                 healthy_http_codes: Optional[List[int]] = None):
        """
        Constructor.

        :param prefix: A prefix for resource names.
        :param lb_security_groups: Security groups to attach to a loadbalancer. NOTE! when passing loadbalancer
        security groups - make sure the loadbalancer can communicate through ci/cd blue/green deployments
        opened ports. Usually they are 8000 and 44300.
        :param subnets: Subnets in which loadbalancer can exist.
        :param vpc: Virtual private cloud in which target groups and a loadbalancer exist.
        :param desired_domain_name: Domain name for using https.
        :param healthy_http_codes: The deployed instance is constantly pinged to determine if it is available
        (healthy) or not. Specify a list of http codes that your service can return and should be treated as healthy.
        """
        # By default a healthy http code is considered to be 200.
        healthy_http_codes = healthy_http_codes or [200]

        # If your service's task definition uses the awsvpc network mode
        # (which is required for the Fargate launch type), you must choose ip as the target type,
        # not instance, when creating your target groups because
        # tasks that use the awsvpc network mode are associated with an elastic network interface,
        # not an Amazon EC2 instance.
        self.target_type = 'ip'

        # Certificate so a loadbalancer could communicate via HTTPS.
        self.certificate = Certificate(
            prefix + 'FargateEcsCertificate',
            DomainName=desired_domain_name,
            ValidationMethod='DNS',
        )

        # A main target group to which a loadbalancer forwards a HTTP traffic.
        # This is the main group with which our ecs container is associated.
        self.target_group_1_http = TargetGroup(
            prefix + 'FargateEcsTargetGroup1',
            Name=prefix + 'FargateEcsTargetGroup1',
            Matcher=Matcher(
                HttpCode=','.join([str(code) for code in healthy_http_codes])),
            Port=self.TARGET_GROUP_PORT,
            Protocol='HTTP',
            VpcId=Ref(vpc),
            TargetType=self.target_type)

        # Second target group is usd for Blue/Green deployments. A new container (that should be deployed)
        # is associated with the second target group.
        self.target_group_2_http = TargetGroup(
            prefix + 'FargateEcsTargetGroup2',
            Name=prefix + 'FargateEcsTargetGroup2',
            Matcher=Matcher(
                HttpCode=','.join([str(code) for code in healthy_http_codes])),
            Port=self.TARGET_GROUP_PORT,
            Protocol='HTTP',
            VpcId=Ref(vpc),
            TargetType=self.target_type)

        self.load_balancer = LoadBalancer(
            prefix + 'FargateEcsLoadBalancer',
            Subnets=[Ref(sub) for sub in subnets],
            SecurityGroups=[Ref(group) for group in lb_security_groups],
            Name=prefix + 'FargateEcsLoadBalancer',
            Scheme='internet-facing',
        )

        self.load_balancer_output = Output(
            prefix + 'FargateEcsLoadBalancerUrl',
            Description='The endpoint url of a loadbalancer.',
            Value=GetAtt(self.load_balancer, 'DNSName'))

        # Listener that listens to HTTP incoming traffic and redirects to other HTTPS listener.
        self.listener_http_1 = Listener(
            prefix + 'FargateEcsHttpListener1',
            Port=self.LISTENER_HTTP_PORT_1,
            Protocol='HTTP',
            LoadBalancerArn=Ref(self.load_balancer),
            DefaultActions=[
                # Redirect to https.
                Action(Type='redirect',
                       RedirectConfig=RedirectConfig(
                           Host='#{host}',
                           Path='/#{path}',
                           Port=str(self.LISTENER_HTTPS_PORT_1),
                           Query='#{query}',
                           StatusCode='HTTP_301',
                           Protocol='HTTPS'))
            ])

        # Listener that listens to HTTPS traffic and forwards to a target group.
        self.listener_https_1 = Listener(
            prefix + 'FargateEcsHttpsListener1',
            Certificates=[LBCertificate(CertificateArn=Ref(self.certificate))],
            Port=self.LISTENER_HTTPS_PORT_1,
            Protocol='HTTPS',
            LoadBalancerArn=Ref(self.load_balancer),
            DefaultActions=[
                Action(Type='forward',
                       TargetGroupArn=Ref(self.target_group_1_http))
            ])

        # Second listener is usd for Blue/Green deployments (testing new instance). Test HTTP traffic is
        # redirected to test HTTPS traffic.
        self.listener_http_2 = Listener(
            prefix + 'FargateEcsHttpListener2',
            Port=self.LISTENER_HTTP_PORT_2,
            Protocol='HTTP',
            LoadBalancerArn=Ref(self.load_balancer),
            DefaultActions=[
                # Redirect to https.
                Action(Type='redirect',
                       RedirectConfig=RedirectConfig(
                           Host='#{host}',
                           Path='/#{path}',
                           Port=str(self.LISTENER_HTTPS_PORT_2),
                           Query='#{query}',
                           StatusCode='HTTP_301',
                           Protocol='HTTPS'))
            ])

        # Listener that listens to test HTTP traffic and forwards to a secondary target group (new container).
        self.listener_https_2 = Listener(
            prefix + 'FargateEcsHttpsListener2',
            Certificates=[LBCertificate(CertificateArn=Ref(self.certificate))],
            Port=self.LISTENER_HTTPS_PORT_2,
            Protocol='HTTPS',
            LoadBalancerArn=Ref(self.load_balancer),
            DefaultActions=[
                Action(Type='forward',
                       TargetGroupArn=Ref(self.target_group_2_http))
            ])
Example #12
0
    def test_using_load_balancer(self):
        test_stack_name = "TestALB"
        init_cf_env(test_stack_name)
        ###

        t = Template()
        load_balancer_sg = ts_add_security_group(
            t, name="LoadBalancerSecurityGroup")
        instance_sg = ts_add_security_group(t)

        load_balancer = t.add_resource(
            LoadBalancer(
                "MyLoadBalancer",
                SecurityGroups=[Ref(load_balancer_sg)],
                # The ALB is publicly accessible.
                # (use `internal` instead of `internet-facing` to define a load balancer reachable from private network only)
                Scheme='internet-facing',
                Subnets=[get_subnet(index=0),
                         get_subnet(index=1)
                         ],  # Attaches the ALB to the subnets
                Type='application'))
        target_group = t.add_resource(
            TargetGroup(
                "MyTargetGroup",
                HealthCheckIntervalSeconds=10,
                HealthCheckProtocol='HTTP',
                HealthCheckPath='/index.html',
                HealthCheckTimeoutSeconds=5,
                HealthyThresholdCount=3,
                UnhealthyThresholdCount=2,
                Matcher=Matcher(
                    HttpCode='200-299'
                ),  # If HTTP status code is 2XX, the backend is considered healthy.
                Port=
                80,  # The web server on the EC2 instances listens on port 80.
                Protocol='HTTP',
                VpcId=get_default_vpc(),
            ))
        listener = t.add_resource(
            Listener(
                "MyListener",
                LoadBalancerArn=Ref(load_balancer),
                Port=80,
                Protocol=
                'HTTP',  # The load balancer listens on port 80 for HTTP requests.
                DefaultActions=[
                    Action(
                        Type='forward',
                        # TargetGroupARN is the connection between the ALB and the auto-scaling group
                        TargetGroupArn=Ref(target_group),
                    )
                ]))

        launch_config = t.add_resource(
            LaunchConfiguration(
                "MyLaunchConfiguration",
                ImageId=get_linux2_image_id(),
                InstanceType='m4.xlarge',
                KeyName=KEY,
                SecurityGroups=[Ref(instance_sg)],
                AssociatePublicIpAddress=True,
                InstanceMonitoring=False,
                UserData=Base64(
                    Join('', [
                        '#!/bin/bash -xe\n',
                        '/opt/aws/bin/cfn-init -v --stack ',
                        Ref('AWS::StackName'),
                        '                         --resource MyLaunchConfiguration ',
                        '                         --region ',
                        Ref('AWS::Region'), '\n'
                    ])),
                Metadata=Metadata(
                    Init({
                        'config':
                        InitConfig(
                            packages={'yum': {
                                'httpd': []
                            }},
                            files={
                                '/tmp/config': {
                                    'content':
                                    Join('\n', [
                                        '#!/bin/bash -ex',
                                        'PRIVATE_IP=`curl -s http://169.254.169.254/latest/meta-data/local-ipv4`',
                                        'echo "$PRIVATE_IP" > index.html',
                                    ]),
                                    'mode':
                                    '000500',
                                    'owner':
                                    'root',
                                    'group':
                                    'root',
                                }
                            },
                            commands={
                                '01_config': {
                                    'command': "/tmp/config",
                                    'cwd': '/var/www/html'
                                }
                            },
                            services={
                                'sysvinit': {
                                    'httpd': {
                                        'enabled': True,
                                        'ensureRunning': True
                                    }
                                }
                            })
                    }))))
        auto_scaling_group = t.add_resource(
            AutoScalingGroup(
                "MyAutoScalingGroup",
                LaunchConfigurationName=Ref(launch_config),
                DesiredCapacity=2,
                MinSize=2,
                MaxSize=2,
                VPCZoneIdentifier=[get_subnet(index=0),
                                   get_subnet(index=1)],
                TargetGroupARNs=[
                    Ref(target_group)
                ],  # Registers new EC2 instances with the default target group.
                Tags=[
                    Tag(
                        "Name", test_stack_name, True
                    )  # 'True' means: Attaches the same tags to the virtual machine started by this auto-scaling group
                ]))

        t.add_output([
            Output("URL", Value=Sub('http://${MyLoadBalancer.DNSName}')),
        ])
        dump_template(t, True)
        create_stack(test_stack_name, t)
        outputs = get_stack_outputs(test_stack_name)
        lb_url = get_output_value(outputs, 'URL')
        private_ips = set()
        for i in range(10):
            private_ips.add(run(f'curl {lb_url}', True))
        self.assertEqual(len(private_ips), 2)
Example #13
0
    def to_json(self):
        if self._json is not None:
            return self._json

        # Validity checks
        if len(self.subnet_ids) < 2:
            raise ValidationException(
                "Use .subnet_id() to specify at least two ELB subnets")
        if len(self.cert_ids) < 1:
            raise ValidationException(
                "Use .certificate_id() to specify at least one certificate")
        if not self._ecs_redirect and len(self.default_targets) < 1:
            raise ValidationException(
                "Use .default_target() to specify at least one default target or .ecs_redirect("
                ") to set up a redirect container")
        for (name, tp) in self.target_paths.iteritems():
            if len(set(map(lambda h: h.type, tp.hosts))) != 1:
                raise ValidationException(
                    "Inconsistent target types for %s. All hosts for a given path must have the "
                    "same type (ip or instance)." % name)

        # Build Security Group
        if self._custom_elb_sgs:
            elb_sgs = self._custom_elb_sgs
        else:
            elb_sg = SecurityGroup(
                "ElbSecurityGroup",
                GroupDescription=Sub("${AWS::StackName}-ElbSg"),
                Tags=self.tags_with(Name=Sub("${AWS::StackName}-ElbSg")),
                VpcId=self.vpc_id,
                SecurityGroupEgress=[
                    SecurityGroupRule(CidrIp="0.0.0.0/0", IpProtocol="-1")
                ],
                SecurityGroupIngress=self._sg_rules)
            self.template.add_resource(elb_sg)
            self.template.add_output(
                Output("ElbSecurityGroupOutput",
                       Description="Security group ID assigned to the ELB",
                       Value=Ref(elb_sg),
                       Export=Export(Sub("${AWS::StackName}-ElbSg"))))

            # Build Attachment Security Group
            inst_sg = SecurityGroup(
                "InstanceSecurityGroup",
                GroupDescription=Sub("${AWS::StackName}-InstSg"),
                Tags=self.tags_with(Name=Sub("${AWS::StackName}-InstSg")),
                VpcId=self.vpc_id,
                SecurityGroupEgress=[
                    SecurityGroupRule(CidrIp="0.0.0.0/0", IpProtocol="-1")
                ],
                SecurityGroupIngress=[
                    SecurityGroupRule(IpProtocol="-1",
                                      SourceSecurityGroupId=Ref(elb_sg))
                ])
            self.template.add_resource(inst_sg)
            self.template.add_output(
                Output("InstanceSecurityGroupOutput",
                       Description="Convenience SG to assign to instances",
                       Value=Ref(inst_sg),
                       Export=Export(Sub("${AWS::StackName}-InstSg"))))
            elb_sgs = [Ref("ElbSecurityGroup")]

        # Build ELB
        elb = LoadBalancer("ELB",
                           Name=Ref("AWS::StackName"),
                           SecurityGroups=elb_sgs,
                           Subnets=self.subnet_ids,
                           Tags=self.tags_with(Name=Ref("AWS::StackName")),
                           LoadBalancerAttributes=self.elb_attributes())
        self.template.add_resource(elb)
        self.template.add_output(
            Output("ElbArnOutput",
                   Description="ARN of the ELB",
                   Value=Ref(elb),
                   Export=Export(Sub("${AWS::StackName}-ElbArn"))))
        self.template.add_output(
            Output("ElbDnsOutput",
                   Description="DNS name of the ELB",
                   Value=GetAtt("ELB", "DNSName"),
                   Export=Export(Sub("${AWS::StackName}-ElbDns"))))

        # Build Default Target Group
        if self._ecs_redirect:
            default_tg_protocol = "HTTP"
        else:
            default_tg_protocol = self.default_targets[0].protocol
        default_tg = TargetGroup(
            "DefaultTargetGroup",
            Port=8080,
            Protocol=default_tg_protocol,
            Tags=self.tags_with(Name=Sub("${AWS::StackName}-Default")),
            VpcId=self.vpc_id,
            Targets=list(
                map(lambda h: TargetDescription(Id=h.host, Port=h.port),
                    self.default_targets)),
            HealthyThresholdCount=2,
            Matcher=Matcher(HttpCode="200-399"))
        self.template.add_resource(default_tg)
        self.attach_alarm(default_tg)

        # Build Listener
        self.template.add_resource(
            Listener("HttpsListener",
                     Certificates=list(
                         map(lambda i: Certificate(CertificateArn=i),
                             self.cert_ids)),
                     DefaultActions=[
                         Action(Type="forward",
                                TargetGroupArn=Ref("DefaultTargetGroup"))
                     ],
                     LoadBalancerArn=Ref("ELB"),
                     Port=443,
                     Protocol="HTTPS"))

        # Build HTTP redirect
        if len(self.http_redirect_targets) > 0:
            # Build Redirect Target Group
            http_tg = TargetGroup(
                "RedirectTargetGroup",
                Port=8080,
                Protocol=self.http_redirect_targets[0].protocol,
                Tags=self.tags_with(Name=Sub("${AWS::StackName}-Redirect")),
                VpcId=self.vpc_id,
                Targets=list(
                    map(lambda h: TargetDescription(Id=h.host, Port=h.port),
                        self.http_redirect_targets)),
                HealthyThresholdCount=2,
                Matcher=Matcher(HttpCode="200-399"))
            self.template.add_resource(http_tg)
            self.attach_alarm(http_tg)

        if self._ecs_redirect or len(self.http_redirect_targets) > 0:
            if self._ecs_redirect:
                redirect_tg = "DefaultTargetGroup"
            else:
                redirect_tg = "RedirectTargetGroup"
            # Build Listener
            self.template.add_resource(
                Listener("HttpListener",
                         DefaultActions=[
                             Action(Type="forward",
                                    TargetGroupArn=Ref(redirect_tg))
                         ],
                         LoadBalancerArn=Ref("ELB"),
                         Port=80,
                         Protocol="HTTP"))

        # Build Target Groups & Rules
        for (name, tp) in self.target_paths.iteritems():
            name_an = alpha_numeric_name(name)
            tag_name = taggable_name(name)

            g = TargetGroup(
                "PathTg" + name_an,
                Port=tp.hosts[0].port,
                Protocol=tp.hosts[0].protocol,
                Tags=self.tags_with(Name="%s/%s" % (self.env_name, tag_name),
                                    TargetPath=tag_name),
                Targets=list(map(lambda h: h.to_target_desc(), tp.hosts)),
                VpcId=self.vpc_id,
                HealthCheckPath="/%s" % name,
                HealthyThresholdCount=2,
                Matcher=tp.health_check_matcher)

            # TODO: We should probably explicitly specify this for every TG. Not
            #       doing that now because it will cause lots of updates. Maybe
            #       in 0.4?
            if len(tp.hosts) > 0 and tp.hosts[0].type != "instance":
                g.TargetType = tp.hosts[0].type

            if self.sticky:
                g.TargetGroupAttributes = [
                    TargetGroupAttribute(Key="stickiness.enabled",
                                         Value="true"),
                    TargetGroupAttribute(Key="stickiness.type",
                                         Value="lb_cookie")
                ]
            self.template.add_resource(g)
            self.attach_alarm(g)
            self.template.add_resource(
                ListenerRule(
                    "PathRl" + name_an,
                    Actions=[Action(Type="forward", TargetGroupArn=Ref(g))],
                    Conditions=[
                        Condition(Field="path-pattern",
                                  Values=["/%s/*" % name])
                    ],
                    ListenerArn=Ref("HttpsListener"),
                    Priority=self.priority_hash(name)))
            self.template.add_resource(
                ListenerRule(
                    "PathRln" + name_an,
                    Actions=[Action(Type="forward", TargetGroupArn=Ref(g))],
                    Conditions=[
                        Condition(Field="path-pattern", Values=["/%s" % name])
                    ],
                    ListenerArn=Ref("HttpsListener"),
                    Priority=self.priority_hash(name)))

        # Build Alternate Listeners
        for al in self.alt_listeners:
            tg_name = "AltTg%d" % al.port
            tg_protocol = al.hosts[0].protocol
            tg = TargetGroup(
                tg_name,
                Port=9999,
                Protocol=tg_protocol,
                Tags=self.tags_with(Name=Sub("${AWS::StackName}-%s" %
                                             tg_name)),
                VpcId=self.vpc_id,
                Targets=list(
                    map(lambda h: TargetDescription(Id=h.host, Port=h.port),
                        al.hosts)),
                HealthyThresholdCount=2,
                Matcher=Matcher(HttpCode="200-399"))
            self.template.add_resource(tg)
            self.attach_alarm(tg)

            listener = Listener("AltListener%d" % al.port,
                                DefaultActions=[
                                    Action(Type="forward",
                                           TargetGroupArn=Ref(tg_name))
                                ],
                                LoadBalancerArn=Ref("ELB"),
                                Port=al.port,
                                Protocol=al.protocol)

            if al.protocol == "HTTPS":
                listener.Certificates = list(
                    map(lambda i: Certificate(CertificateArn=i),
                        self.cert_ids))

            self.template.add_resource(listener)

        self._json = self.template.to_json()
        return self._json
Example #14
0
def build_template(sierrafile):
    template = Template()

    template.add_version('2010-09-09')

    template.add_metadata(build_interface(sierrafile.extra_params))

    parameters = AttrDict(

        # Network Parameters

        vpc_cidr=template.add_parameter(Parameter(
            'VpcCidr',
            Type='String',
            Default='192.172.0.0/16',
        )),
        subnet1_cidr=template.add_parameter(Parameter(
            'Subnet1Cidr',
            Type='String',
            Default='192.172.1.0/24',
        )),
        subnet2_cidr=template.add_parameter(Parameter(
            'Subnet2Cidr',
            Type='String',
            Default='192.172.2.0/24',
        )),

        # ECS Parameters

        cluster_size=template.add_parameter(Parameter(
            'ClusterSize',
            Type='Number',
            Default=2,
        )),
        instance_type=template.add_parameter(Parameter(
            'InstanceType',
            Type='String',
            Default='t2.medium'
        )),
        key_name=template.add_parameter(Parameter(
            'KeyName',
            Type='AWS::EC2::KeyPair::KeyName',
        )),
        image_id=template.add_parameter(Parameter(
            'ImageId',
            Type='AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>',
            Default=(
                '/aws/service/ecs/optimized-ami'
                '/amazon-linux/recommended/image_id'
            ),
            Description=(
              'An SSM parameter that resolves to a valid AMI ID.'
              ' This is the AMI that will be used to create ECS hosts.'
              ' The default is the current recommended ECS-optimized AMI.'
            )
        )),

        # Other Parameters

        github_token=template.add_parameter(Parameter(
            'GitHubToken',
            Type='String',
            NoEcho=True,
        )),
    )

    # Environment Variable Parameters

    for env_var_param, env_var_name in sierrafile.extra_params:
        template.add_parameter(Parameter(
            env_var_param,
            Type='String',
            NoEcho=True,
        ))

    # Resource Declarations

    # # Network

    network_vpc = template.add_resource(VPC(
        'NetworkVpc',
        CidrBlock=Ref(parameters.vpc_cidr),
        Tags=Tags(Name=Ref('AWS::StackName')),
    ))

    network_ig = template.add_resource(InternetGateway(
        'NetworkInternetGateway',
        Tags=Tags(Name=Ref('AWS::StackName')),
    ))

    vpc_attach = template.add_resource(VPCGatewayAttachment(
        'NetworkInternetGatewayAttachment',
        InternetGatewayId=Ref(network_ig),
        VpcId=Ref(network_vpc),
    ))

    route_table = template.add_resource(RouteTable(
        'NetworkRouteTable',
        VpcId=Ref(network_vpc),
        Tags=Tags(Name=Ref('AWS::StackName')),
    ))

    template.add_resource(Route(
        'NetworkDefaultRoute',
        DependsOn=[vpc_attach.title],
        RouteTableId=Ref(route_table),
        DestinationCidrBlock='0.0.0.0/0',
        GatewayId=Ref(network_ig),
    ))

    subnet1 = template.add_resource(Subnet(
        'NetworkSubnet1',
        VpcId=Ref(network_vpc),
        AvailabilityZone=Select(0, GetAZs()),
        MapPublicIpOnLaunch=True,
        CidrBlock=Ref(parameters.subnet1_cidr),
        Tags=Tags(Name=Sub('${AWS::StackName} (Public)')),
    ))

    subnet2 = template.add_resource(Subnet(
        'NetworkSubnet2',
        VpcId=Ref(network_vpc),
        AvailabilityZone=Select(1, GetAZs()),
        MapPublicIpOnLaunch=True,
        CidrBlock=Ref(parameters.subnet2_cidr),
        Tags=Tags(Name=Sub('${AWS::StackName} (Public)')),
    ))

    template.add_resource(SubnetRouteTableAssociation(
        'NetworkSubnet1RouteTableAssociation',
        RouteTableId=Ref(route_table),
        SubnetId=Ref(subnet1),
    ))

    template.add_resource(SubnetRouteTableAssociation(
        'NetworkSubnet2RouteTableAssociation',
        RouteTableId=Ref(route_table),
        SubnetId=Ref(subnet2),
    ))

    elb = template.add_resource(LoadBalancer(
        ELB_NAME,
        Name=Sub('${AWS::StackName}-elb'),
        Type='network',
        Subnets=[Ref(subnet1), Ref(subnet2)],
    ))

    # # Cluster

    ecs_host_role = template.add_resource(Role(
        'EcsHostRole',
        AssumeRolePolicyDocument=PolicyDocument(
            Statement=[Statement(
                Effect=Allow,
                Principal=Principal('Service', 'ec2.amazonaws.com'),
                Action=[awacs.sts.AssumeRole]
            )],
        ),
        ManagedPolicyArns=[
            'arn:aws:iam::aws:policy/'
            'service-role/AmazonEC2ContainerServiceforEC2Role'
        ]
    ))

    ecs_host_profile = template.add_resource(InstanceProfile(
        'EcsHostInstanceProfile',
        Roles=[Ref(ecs_host_role)]
    ))

    ecs_host_sg = template.add_resource(SecurityGroup(
        'EcsHostSecurityGroup',
        GroupDescription=Sub('${AWS::StackName}-hosts'),
        VpcId=Ref(network_vpc),
        SecurityGroupIngress=[SecurityGroupRule(
            CidrIp='0.0.0.0/0',
            IpProtocol='-1'
        )]
    ))

    cluster = template.add_resource(Cluster(
        'EcsCluster',
        ClusterName=Ref('AWS::StackName')
    ))

    autoscaling_name = 'EcsHostAutoScalingGroup'
    launch_conf_name = 'EcsHostLaunchConfiguration'

    launch_conf = template.add_resource(LaunchConfiguration(
        launch_conf_name,
        ImageId=Ref(parameters.image_id),
        InstanceType=Ref(parameters.instance_type),
        IamInstanceProfile=Ref(ecs_host_profile),
        KeyName=Ref(parameters.key_name),
        SecurityGroups=[Ref(ecs_host_sg)],
        UserData=Base64(Sub(
            '#!/bin/bash\n'
            'yum install -y aws-cfn-bootstrap\n'
            '/opt/aws/bin/cfn-init -v'
            ' --region ${AWS::Region}'
            ' --stack ${AWS::StackName}'
            f' --resource {launch_conf_name}\n'
            '/opt/aws/bin/cfn-signal -e $?'
            ' --region ${AWS::Region}'
            ' --stack ${AWS::StackName}'
            f' --resource {autoscaling_name}\n'
        )),
        Metadata={
            'AWS::CloudFormation::Init': {
                'config': {
                    'commands': {
                        '01_add_instance_to_cluster': {
                            'command': Sub(
                                f'echo ECS_CLUSTER=${{{cluster.title}}}'
                                f' > /etc/ecs/ecs.config'
                            ),
                        }
                    },
                    'files': {
                        '/etc/cfn/cfn-hup.conf': {
                            'mode': 0o400,
                            'owner': 'root',
                            'group': 'root',
                            'content': Sub(
                                '[main]\n'
                                'stack=${AWS::StackId}\n'
                                'region=${AWS::Region}\n'
                            ),
                        },
                        '/etc/cfn/hooks.d/cfn-auto-reloader.conf': {
                            'content': Sub(
                                '[cfn-auto-reloader-hook]\n'
                                'triggers=post.update\n'
                                'path=Resources.ContainerInstances.Metadata'
                                '.AWS::CloudFormation::Init\n'
                                'action=/opt/aws/bin/cfn-init -v'
                                ' --region ${AWS::Region}'
                                ' --stack ${AWS::StackName}'
                                f' --resource {launch_conf_name}\n'
                            ),
                        },
                    },
                    'services': {
                        'sysvinit': {
                            'cfn-hup': {
                                'enabled': True,
                                'ensureRunning': True,
                                'files': [
                                    '/etc/cfn/cfn-hup.conf',
                                    '/etc/cfn/hooks.d/cfn-auto-reloader.conf'
                                ]
                            }
                        }
                    }
                }
            }
        }
    ))

    autoscaling_group = template.add_resource(AutoScalingGroup(
        autoscaling_name,
        VPCZoneIdentifier=[Ref(subnet1), Ref(subnet2)],
        LaunchConfigurationName=Ref(launch_conf),
        DesiredCapacity=Ref(parameters.cluster_size),
        MinSize=Ref(parameters.cluster_size),
        MaxSize=Ref(parameters.cluster_size),
        Tags=[{
            'Key': 'Name',
            'Value': Sub('${AWS::StackName} - ECS Host'),
            'PropagateAtLaunch': True,
        }],
        CreationPolicy=CreationPolicy(
            ResourceSignal=ResourceSignal(Timeout='PT15M'),
        ),
        UpdatePolicy=UpdatePolicy(
            AutoScalingRollingUpdate=AutoScalingRollingUpdate(
                MinInstancesInService=1,
                MaxBatchSize=1,
                PauseTime='PT5M',
                WaitOnResourceSignals=True,
            ),
        ),
    ))

    # # Services

    task_role = template.add_resource(Role(
        'TaskExecutionRole',
        AssumeRolePolicyDocument=PolicyDocument(
            Statement=[Statement(
                Effect=Allow,
                Principal=Principal('Service', 'ecs-tasks.amazonaws.com'),
                Action=[awacs.sts.AssumeRole],
            )],
        ),
        ManagedPolicyArns=[
            'arn:aws:iam::aws:policy/'
            'service-role/AmazonECSTaskExecutionRolePolicy'
        ],
    ))

    artifact_bucket = template.add_resource(Bucket(
        'ArtifactBucket',
        DeletionPolicy='Retain',
    ))

    codebuild_role = template.add_resource(Role(
        'CodeBuildServiceRole',
        Path='/',
        AssumeRolePolicyDocument=PolicyDocument(
            Version='2012-10-17',
            Statement=[
                Statement(
                    Effect=Allow,
                    Principal=Principal(
                        'Service', 'codebuild.amazonaws.com'
                    ),
                    Action=[
                        awacs.sts.AssumeRole,
                    ],
                ),
            ],
        ),
        Policies=[Policy(
            PolicyName='root',
            PolicyDocument=PolicyDocument(
                Version='2012-10-17',
                Statement=[
                    Statement(
                        Resource=['*'],
                        Effect=Allow,
                        Action=[
                            awacs.ssm.GetParameters,
                        ],
                    ),
                    Statement(
                        Resource=['*'],
                        Effect=Allow,
                        Action=[
                            awacs.s3.GetObject,
                            awacs.s3.PutObject,
                            awacs.s3.GetObjectVersion,
                        ],
                    ),
                    Statement(
                        Resource=['*'],
                        Effect=Allow,
                        Action=[
                            awacs.logs.CreateLogGroup,
                            awacs.logs.CreateLogStream,
                            awacs.logs.PutLogEvents,
                        ],
                    ),
                ],
            ),
        )],
    ))

    codepipeline_role = template.add_resource(Role(
        'CodePipelineServiceRole',
        Path='/',
        AssumeRolePolicyDocument=PolicyDocument(
            Version='2012-10-17',
            Statement=[
                Statement(
                    Effect=Allow,
                    Principal=Principal(
                        'Service', 'codepipeline.amazonaws.com'
                    ),
                    Action=[
                        awacs.sts.AssumeRole,
                    ],
                ),
            ],
        ),
        Policies=[Policy(
            PolicyName='root',
            PolicyDocument=PolicyDocument(
                Version='2012-10-17',
                Statement=[
                    Statement(
                        Resource=[
                            Sub(f'${{{artifact_bucket.title}.Arn}}/*')
                        ],
                        Effect=Allow,
                        Action=[
                            awacs.s3.GetBucketVersioning,
                            awacs.s3.GetObject,
                            awacs.s3.GetObjectVersion,
                            awacs.s3.PutObject,
                        ],
                    ),
                    Statement(
                        Resource=['*'],
                        Effect=Allow,
                        Action=[
                            awacs.ecs.DescribeServices,
                            awacs.ecs.DescribeTaskDefinition,
                            awacs.ecs.DescribeTasks,
                            awacs.ecs.ListTasks,
                            awacs.ecs.RegisterTaskDefinition,
                            awacs.ecs.UpdateService,
                            awacs.codebuild.StartBuild,
                            awacs.codebuild.BatchGetBuilds,
                            awacs.iam.PassRole,
                        ],
                    ),
                ],
            ),
        )],
    ))

    log_group = template.add_resource(LogGroup(
        'LogGroup',
        LogGroupName=Sub('/ecs/${AWS::StackName}'),
    ))

    if any(conf.pipeline.enable for conf in sierrafile.services.values()):
        project = template.add_resource(Project(
            'CodeBuildProject',
            Name=Sub('${AWS::StackName}-build'),
            ServiceRole=Ref(codebuild_role),
            Artifacts=Artifacts(Type='CODEPIPELINE'),
            Source=Source(Type='CODEPIPELINE'),
            Environment=Environment(
                ComputeType='BUILD_GENERAL1_SMALL',
                Image='aws/codebuild/docker:17.09.0',
                Type='LINUX_CONTAINER',
            ),
        ))

    for name, settings in sierrafile.services.items():
        task_definition = template.add_resource(TaskDefinition(
            f'{name}TaskDefinition',
            RequiresCompatibilities=['EC2'],
            Cpu=str(settings.container.cpu),
            Memory=str(settings.container.memory),
            NetworkMode='bridge',
            ExecutionRoleArn=Ref(task_role.title),
            ContainerDefinitions=[
                ContainerDefinition(
                    Name=f'{name}',
                    Image=settings.container.image,
                    Memory=str(settings.container.memory),
                    Essential=True,
                    PortMappings=[
                        PortMapping(
                            ContainerPort=settings.container.port,
                            Protocol='tcp',
                        ),
                    ],
                    Environment=[
                        troposphere.ecs.Environment(Name=k, Value=v)
                        for k, v in sierrafile.env_vars.items()
                        if k in settings.get('environment', [])
                    ],
                    LogConfiguration=LogConfiguration(
                        LogDriver='awslogs',
                        Options={
                            'awslogs-region': Ref('AWS::Region'),
                            'awslogs-group': Ref(log_group.title),
                            'awslogs-stream-prefix': Ref('AWS::StackName'),
                        },
                    ),
                ),
            ],
        ))

        target_group = template.add_resource(TargetGroup(
            f'{name}TargetGroup',
            Port=settings.container.port,
            Protocol='TCP',
            VpcId=Ref(network_vpc),
            Tags=Tags(Name=Sub(f'${{AWS::StackName}}-{name}')),
        ))

        listener = template.add_resource(Listener(
            f'{name}ElbListener',
            LoadBalancerArn=Ref(elb),
            Port=settings.container.port,
            Protocol='TCP',
            DefaultActions=[
                Action(TargetGroupArn=Ref(target_group), Type='forward')
            ],
        ))

        service = template.add_resource(Service(
            f'{name}Service',
            Cluster=Ref(cluster),
            ServiceName=f'{name}-service',
            DependsOn=[autoscaling_group.title, listener.title],
            DesiredCount=settings.container.count,
            TaskDefinition=Ref(task_definition),
            LaunchType='EC2',
            LoadBalancers=[
                troposphere.ecs.LoadBalancer(
                    ContainerName=f'{name}',
                    ContainerPort=settings.container.port,
                    TargetGroupArn=Ref(target_group),
                ),
            ],
        ))

        if settings.pipeline.enable:
            pipeline = template.add_resource(Pipeline(
                f'{name}Pipeline',
                RoleArn=GetAtt(codepipeline_role, 'Arn'),
                ArtifactStore=ArtifactStore(
                    Type='S3',
                    Location=Ref(artifact_bucket),
                ),
                Stages=[
                    Stages(
                        Name='Source',
                        Actions=[Actions(
                            Name='Source',
                            ActionTypeId=ActionTypeId(
                                Category='Source',
                                Owner='ThirdParty',
                                Version='1',
                                Provider='GitHub',
                            ),
                            OutputArtifacts=[
                                OutputArtifacts(Name=f'{name}Source'),
                            ],
                            RunOrder='1',
                            Configuration={
                                'Owner': settings.pipeline.user,
                                'Repo': settings.pipeline.repo,
                                'Branch': settings.pipeline.branch,
                                'OAuthToken': Ref(parameters.github_token),
                            },
                        )],
                    ),
                    Stages(
                        Name='Build',
                        Actions=[Actions(
                            Name='Build',
                            ActionTypeId=ActionTypeId(
                                Category='Build',
                                Owner='AWS',
                                Version='1',
                                Provider='CodeBuild',
                            ),
                            InputArtifacts=[
                                InputArtifacts(Name=f'{name}Source'),
                            ],
                            OutputArtifacts=[
                                OutputArtifacts(Name=f'{name}Build'),
                            ],
                            RunOrder='1',
                            Configuration={
                                'ProjectName': Ref(project),
                            },
                        )],
                    ),
                    Stages(
                        Name='Deploy',
                        Actions=[Actions(
                            Name='Deploy',
                            ActionTypeId=ActionTypeId(
                                Category='Deploy',
                                Owner='AWS',
                                Version='1',
                                Provider='ECS',
                            ),
                            InputArtifacts=[
                                InputArtifacts(Name=f'{name}Build')
                            ],
                            RunOrder='1',
                            Configuration={
                                'ClusterName': Ref(cluster),
                                'ServiceName': Ref(service),
                                'FileName': 'image.json',
                            },
                        )],
                    ),
                ],
            ))

            template.add_resource(Webhook(
                f'{name}CodePipelineWebhook',
                Name=Sub(f'${{AWS::StackName}}-{name}-webhook'),
                Authentication='GITHUB_HMAC',
                AuthenticationConfiguration=AuthenticationConfiguration(
                    SecretToken=Ref(parameters.github_token),
                ),
                Filters=[FilterRule(
                    JsonPath='$.ref',
                    MatchEquals=f'refs/heads/{settings.pipeline.branch}'
                )],
                TargetAction='Source',
                TargetPipeline=Ref(pipeline),
                TargetPipelineVersion=1,
                RegisterWithThirdParty=True,
            ))

    return template
Example #15
0
                                            Value='60'),
                 ],
                 Scheme='internet-facing',
                 SecurityGroups=[Ref(api_elb_sg)],
                 Subnets=Ref(subnets)))

target_group = template.add_resource(
    TargetGroup(
        'DefaultTargetGroup',
        Name=Join('-', ['api', 'default', Ref(version)]),
        HealthCheckIntervalSeconds=5,
        HealthCheckProtocol='HTTP',
        HealthCheckTimeoutSeconds=2,
        HealthCheckPath='/',
        HealthyThresholdCount=2,
        UnhealthyThresholdCount=3,
        Matcher=Matcher(HttpCode='200'),
        Port=8080,
        Protocol='HTTP',
        TargetGroupAttributes=[
            TargetGroupAttribute(Key='deregistration_delay.timeout_seconds',
                                 Value='120')
        ],
        VpcId=Ref(vpc_id)))

listener = template.add_resource(
    Listener('HttpListener',
             Port=80,
             Protocol='HTTP',
             LoadBalancerArn=Ref(load_balancer),
             DefaultActions=[
Example #16
0
def create_alb_template():
    template = Template()

    vpc = template.add_parameter(
        parameter=Parameter(title='Vpc', Type='String'))
    subnet_a = template.add_parameter(
        parameter=Parameter(title='SubnetA', Type='String'))
    subnet_b = template.add_parameter(
        parameter=Parameter(title='SubnetB', Type='String'))
    ec2_instance = template.add_parameter(
        parameter=Parameter(title='Ec2Instance', Type='String'))
    certificate = template.add_parameter(
        parameter=Parameter(title='Certificate', Type='String'))

    security_group = template.add_resource(
        resource=SecurityGroup(title='SampleSecurityGroup',
                               GroupDescription='sample-security-group',
                               SecurityGroupIngress=[{
                                   'IpProtocol': 'tcp',
                                   'FromPort': 80,
                                   'ToPort': 80,
                                   'CidrIp': '0.0.0.0/0'
                               }, {
                                   'IpProtocol': 'tcp',
                                   'FromPort': 443,
                                   'ToPort': 443,
                                   'CidrIp': '0.0.0.0/0'
                               }],
                               VpcId=Ref(vpc)))

    load_balancer = template.add_resource(resource=LoadBalancer(
        title='SampleLoadBalancer',
        Name='sample-alb-https',
        Subnets=[Ref(subnet_a), Ref(subnet_b)],
        SecurityGroups=[Ref(security_group)],
    ))

    target_group = template.add_resource(resource=TargetGroup(
        title='SampleTargetGroup',
        Targets=[TargetDescription(
            Id=Ref(ec2_instance),
            Port=80,
        )],
        VpcId=Ref(vpc),
        Name='sample-target-group-https',
        Port=443,
        Protocol='HTTP',
    ))

    template.add_resource(resource=Listener(
        title='SampleListenerHttps',
        Certificates=[Certificate(CertificateArn=Ref(certificate))],
        DefaultActions=[
            Action(TargetGroupArn=Ref(target_group), Type='forward')
        ],
        LoadBalancerArn=Ref(load_balancer),
        Port=443,
        Protocol='HTTPS',
    ))

    template.add_resource(resource=Listener(
        title='SampleListenerHttp',
        DefaultActions=[
            Action(
                RedirectConfig=RedirectConfig(
                    Host='#{host}',
                    Path='/#{path}',
                    Port='443',
                    Protocol='HTTPS',
                    Query='#{query}',
                    StatusCode='HTTP_301',
                ),
                Type='redirect',
            )
        ],
        LoadBalancerArn=Ref(load_balancer),
        Port=80,
        Protocol='HTTP',
    ))

    with open('./alb.yml', mode='w') as file:
        file.write(template.to_yaml())
Example #17
0
from troposphere import Ref, Template
from troposphere.elasticloadbalancingv2 import Action, Condition, ListenerRule, TargetGroup
from troposphere.ecs import TaskDefinition, Service, ContainerDefinition, PortMapping, LoadBalancer

template = Template(
    "testservice"
)

vpc_id = "vpc-0e2786487ff4f2ef4"
region = "eu-west-1"

test_target_group = TargetGroup(
    "targetgroup",
    Name="service-live",
    Port=80,
    Protocol="HTTP",
    TargetType="instance",
    VpcId=vpc_id
)
template.add_resource(test_target_group)

listener_rule = ListenerRule(
    "listenerrule",
    Actions=[
        Action(
            TargetGroupArn=Ref(test_target_group),
            Type='forward'
        )
    ],
    Conditions=[
        Condition(
Example #18
0
# Create Application Load Balancer
load_balancer = LoadBalancer(
    "exampleloadbalancer",
    Subnets=subnet_ids,
    LoadBalancerAttributes=[
        LoadBalancerAttributes(Key="access_logs.s3.enabled", Value="true"),
        LoadBalancerAttributes(Key="access_logs.s3.bucket", Value="true"),
        LoadBalancerAttributes(Key="access_logs.s3.prefix", Value="alb")
    ],
    SecurityGroups=[Ref(alb_security_group)],
    DependsOn=Ref(logs_bucket_policy))
template.add_resource(load_balancer)
target_group = TargetGroup("exampletargetgroup",
                           VpcId=vpc_id,
                           HealthCheckPath='/',
                           Port='80',
                           Protocol='HTTP')
listener = Listener(
    "examplelistener",
    LoadBalancerArn=Ref(load_balancer),
    Port='443',
    Protocol='HTTPS',
    Certificates=[Certificate(CertificateArn=certificate_arn)],
    DefaultActions=[Action(Type='forward', TargetGroupArn=Ref(target_group))],
    SslPolicy="ELBSecurityPolicy-TLS-1-2-Ext-2018-06")
template.add_resource(target_group)
template.add_resource(listener)

# Setup the ASG & launch config
launch_config = LaunchConfiguration(
Example #19
0
    Subnets=[
        Ref("DMZSubnet1a"),
        Ref("DMZSubnet1b"),
    ],
    SecurityGroups=[
        Ref("LoadBalancerSG")
    ],
))

# Load balancer target group
ALBTargetGroup = t.add_resource(TargetGroup(
    "ALBTargetGroup",
    HealthCheckIntervalSeconds="30",
    HealthCheckProtocol="HTTP",
    HealthCheckTimeoutSeconds="5",
    HealthyThresholdCount="3",
    Name="ALBTargetGroup",
    Port=80,
    Protocol="HTTP",
    UnhealthyThresholdCount="3",
    VpcId=Ref(vpc)
))

alb_listener = t.add_resource(Listener(
    "albListener",
    Port="80",
    Protocol="HTTP",
    LoadBalancerArn=Ref(LoadBalancer),
    DefaultActions=[elb.Action(
        Type="forward",
        TargetGroupArn=Ref(ALBTargetGroup)
    )]
Example #20
0
    "us-east-1": {"AMI": "ami-0a6a36557ea3b9859"},
})


# Target group
application_target_group = TargetGroup(
    'ApplicationTargetGroup',
    template=template,
    VpcId=Ref(vpc),
    Matcher=Matcher(
        HttpCode='200-299',
    ),
    Port=8080,
    Protocol='HTTP',
    HealthCheckIntervalSeconds=15,
    HealthCheckPath='/',
    HealthCheckProtocol='HTTP',
    HealthCheckTimeoutSeconds=5,
    HealthyThresholdCount=2,
    UnhealthyThresholdCount=8,
    TargetGroupAttributes=[
        TargetGroupAttribute(
            Key='stickiness.enabled',
            Value='true',
        )
    ],
)


# Web load balancer
load_balancer_security_group = SecurityGroup(
    "LoadBalancerSecurityGroup",
    def create_wordpress_environment(self):

        template = Template()
        template.add_version('2010-09-09')
        
        # Wordpress preparation: format vpc name and split private and public subnets in two lists
       
        vpc_name_formatted = ''.join(
            e for e in self.private_vpc_name if e.isalnum()).capitalize()

        filter_private_subnets = filter(lambda x : x["type"] == "private", self.private_vpc_subnets)
        filter_public_subnets = filter(lambda x : x["type"] == "public", self.private_vpc_subnets)

        private_subnets = []
        for subnet in filter_private_subnets:
            subnet_name_formatted = ''.join(e for e in subnet["name"] if e.isalnum()).capitalize()

            private_subnets.append(ImportValue("{}{}{}SubnetId".format(self.stage, vpc_name_formatted, subnet_name_formatted)))

        public_subnets = []
        for subnet in filter_public_subnets:
            subnet_name_formatted = ''.join(e for e in subnet["name"] if e.isalnum()).capitalize()

            public_subnets.append(ImportValue("{}{}{}SubnetId".format(self.stage, vpc_name_formatted, subnet_name_formatted)))

        # Instances Security Groups

        web_dmz_security_group = template.add_resource(
            SecurityGroup(
                "{}WebDMZSecurityGroup".format(self.stage),
                GroupName="{}webdmz-sg".format(self.stage),
                VpcId=ImportValue("{}{}VpcId".format(self.stage,vpc_name_formatted)),
                GroupDescription="Enables external http access to EC2 instance(s) that host the webpages",
                SecurityGroupIngress=[
                    SecurityGroupRule(
                        IpProtocol="tcp",
                        FromPort="80",
                        ToPort="80",
                        CidrIp="0.0.0.0/0",
                    ),
                    SecurityGroupRule(
                        IpProtocol="tcp",
                        FromPort="22",
                        ToPort="22",
                        SourceSecurityGroupId=ImportValue("{}BastionHostSecurityGroupID".format(self.stage))
                    )
                ]
            )
        )

        rds_private_security_group = template.add_resource(
            SecurityGroup(
                "{}RdsPrivateSecurityGroup".format(self.stage),
                GroupName="{}rds-private-sg".format(self.stage),
                VpcId=ImportValue("{}{}VpcId".format(self.stage,vpc_name_formatted)),
                GroupDescription="Allow access to the mysql port from the webservers",
                SecurityGroupIngress=[
                    SecurityGroupRule(
                        IpProtocol="tcp",
                        FromPort=self.database_port,
                        ToPort=self.database_port,
                        SourceSecurityGroupId=Ref(web_dmz_security_group)
                    )
                ]
            )
        )

        # S3 Buckets for wordpress content

        bucket_wordpress_code = template.add_resource(
            Bucket(
                "{}BucketWordpressCode".format(self.stage),
                BucketName="{}-wordpress-code".format(self.stage),
                AccessControl=Private
            )
        )

        bucket_wordpress_media_assets = template.add_resource(
            Bucket(
                "{}BucketWordpressMediaAssets".format(self.stage),
                BucketName="{}-wordpress-media-assets".format(self.stage),
                AccessControl=Private
            )
        )

        # Database Instance to store wordpress data

        rds_subnet_group = template.add_resource(
            DBSubnetGroup(
                "{}PrivateRDSSubnetGroup".format(self.stage),
                DBSubnetGroupName="{}private-rds-subnet-group".format(self.stage),
                DBSubnetGroupDescription="Subnets available for the RDS DB Instance",
                SubnetIds=private_subnets
            )
        )

        template.add_resource(
            DBInstance(
                "{}RdsInstance".format(self.stage),
                DBInstanceIdentifier="{}RdsInstance".format(self.stage),
                DBName=self.database_name,
                AllocatedStorage="20",
                DBInstanceClass=self.database_instance_class,
                Engine=self.database_engine,
                EngineVersion=self.database_engine_version,
                MasterUsername=self.database_username,
                MasterUserPassword=self.database_password,
                Port=self.database_port,
                BackupRetentionPeriod=0,
                MultiAZ=self.database_multiaz,
                DBSubnetGroupName=Ref(rds_subnet_group),
                VPCSecurityGroups=[Ref(rds_private_security_group)],
                Tags=Tags(
                    Name=self.database_name_tag
                )
            )
        )

        # Cloudfront Distribution to load images

        cloudfront_origin_access_identity = template.add_resource(
            CloudFrontOriginAccessIdentity(
                "{}CloudfrontOriginAccessIdentity".format(self.stage),
                CloudFrontOriginAccessIdentityConfig=CloudFrontOriginAccessIdentityConfig(
                    "{}CloudFrontOriginAccessIdentityConfig".format(self.stage),
                    Comment="WordPress Origin Access Identity"
                )
            )
        )

        template.add_resource(BucketPolicy(
            "{}BucketWordpressMediaAssetsPolicy".format(self.stage),
            Bucket=Ref(bucket_wordpress_media_assets),
            PolicyDocument={
                "Version": "2008-10-17",
                "Id": "PolicyForCloudFrontPrivateContent",
                "Statement": [
                    {
                        "Sid": "1",
                        "Effect": "Allow",
                        "Principal": {
                            "CanonicalUser": GetAtt(cloudfront_origin_access_identity, 'S3CanonicalUserId')
                        },
                        "Action": "s3:GetObject",
                        "Resource": "arn:aws:s3:::{}-wordpress-media-assets/*".format(self.stage)
                    }
                ]
            }
        ))

        cloudfront_distribution = template.add_resource(
            Distribution(
                "{}CloudfrontDistribution".format(self.stage),
                DistributionConfig=DistributionConfig(
                    Origins=[
                        Origin(
                            Id="MediaAssetsOrigin",
                            DomainName=GetAtt(bucket_wordpress_media_assets, 'DomainName'),
                            S3OriginConfig=S3Origin(
                                OriginAccessIdentity=Join("", [
                                    "origin-access-identity/cloudfront/",
                                    Ref(cloudfront_origin_access_identity)
                                ])
                            )
                        )
                    ],
                    DefaultCacheBehavior=DefaultCacheBehavior(
                        TargetOriginId="MediaAssetsOrigin",
                        ForwardedValues=ForwardedValues(
                            QueryString=False
                        ),
                        ViewerProtocolPolicy="allow-all"
                    ),
                    Enabled=True,
                    HttpVersion='http2'
                )
            )
        )

        # Wordpress EC2 Instances
        
        ''' 
            EC2 Instances types:
                Write node = To make changes to your blog. E.g: add new posts
                Read Nodes = Instances open to the internet for blog reading
        '''

        wordpress_ec2_role = template.add_resource(
            Role(
                "{}WordPressEC2InstanceRole".format(self.stage),
                RoleName="{}WordPressEC2InstanceRole".format(self.stage),
                Path="/",
                AssumeRolePolicyDocument={"Statement": [{
                    "Effect": "Allow",
                    "Principal": {
                        "Service": ["ec2.amazonaws.com"]
                    },
                    "Action": ["sts:AssumeRole"]
                }]},
                Policies=[
                    Policy(
                        PolicyName="S3FullAccess",
                        PolicyDocument={
                            "Statement": [{
                                "Effect": "Allow",
                                "Action": "s3:*",
                                "Resource": "*"
                            }],
                        }
                    )
                ]
            )
        )

        spotfleetrole = template.add_resource(
            Role(
                "{}spotfleetrole".format(self.stage),
                AssumeRolePolicyDocument={
                    "Statement": [
                        {
                            "Action": "sts:AssumeRole",
                            "Principal": {
                                "Service": "spotfleet.amazonaws.com"
                            },
                            "Effect": "Allow",
                            "Sid": ""
                        }
                    ],
                    "Version": "2012-10-17"
                },
                ManagedPolicyArns=[
                    "arn:aws:iam::aws:policy/service-role/AmazonEC2SpotFleetRole"
                ]
            )
        )

        ec2_instance_profile = template.add_resource(
            InstanceProfile(
                "{}WriteWordpressEc2InstanceProfile".format(self.stage),
                Roles=[Ref(wordpress_ec2_role)]
            )
        )

        template.add_resource(
            SpotFleet(
                "{}WriteWordpressEc2Instance".format(self.stage),
                SpotFleetRequestConfigData=SpotFleetRequestConfigData(
                    AllocationStrategy="lowestPrice",
                    IamFleetRole=GetAtt(spotfleetrole,"Arn"),
                    LaunchSpecifications=[LaunchSpecifications(
                        IamInstanceProfile=IamInstanceProfile(
                            Arn=GetAtt(ec2_instance_profile, "Arn")
                        ),
                        ImageId=self.write_instance_image_id,
                        InstanceType=self.write_instance_type,
                        KeyName=self.write_instance_key_name,
                        SecurityGroups=[SecurityGroups(GroupId=Ref(web_dmz_security_group))],
                        SubnetId=next(iter(public_subnets)),
                        UserData=Base64(
                            Join("", [
                                """ #!/bin/bash
                                yum install httpd php php-mysql -y
                                cd /var/www/html
                                echo \"healthy\" > healthy.html
                                wget https://wordpress.org/latest.tar.gz
                                tar -xzf latest.tar.gz
                                cp -r wordpress/* /var/www/html/
                                rm -rf wordpress
                                rm -rf latest.tar.gz
                                chmod -R 755 wp-content
                                chown -R apache:apache wp-content
                                echo -e 'Options +FollowSymlinks \nRewriteEngine on \nrewriterule ^wp-content/uploads/(.*)$ http://""",
                                GetAtt(cloudfront_distribution, 'DomainName'),
                                """/$1 [r=301,nc]' > .htaccess
                                chkconfig httpd on
                                cd /var/www
                                sudo chown -R apache /var/www/html
                                cd html/
                                sudo find . -type d -exec chmod 0755 {} \;
                                sudo find . -type f -exec chmod 0644 {} \;
                                sed -i 's/AllowOverride None/AllowOverride All/g' /etc/httpd/conf/httpd.conf
                                sed -i 's/AllowOverride none/AllowOverride All/g' /etc/httpd/conf/httpd.conf
                                echo -e "*/1 * * * * root aws s3 sync --delete /var/www/html s3://""",
                                Ref(bucket_wordpress_code),
                                """">> /etc/crontab 
                                echo -e "*/1 * * * * root aws s3 sync --delete /var/www/html/wp-content/uploads s3://""",
                                Ref(bucket_wordpress_media_assets),
                                """">> /etc/crontab
                                service httpd start
                                """
                            ])
                        )
                    )],
                    TargetCapacity=1,
                    Type="request"
                )
            )
        )

        template.add_resource(
            LaunchConfiguration(
                "{}WordPressReadLaunchConfiguration".format(self.stage),
                InstanceType=self.read_instance_type,
                ImageId=self.read_instance_image_id,
                KeyName=self.read_instance_key_name,
                LaunchConfigurationName="{}-wordpress-launch-config".format(self.stage),
                SecurityGroups=[Ref(web_dmz_security_group)],
                IamInstanceProfile=Ref(ec2_instance_profile),
                SpotPrice="0.5",
                UserData=Base64(
                    Join("", [
                        """ #!/bin/bash
                        yum install httpd php php-mysql -y
                        cd /var/www/html
                        echo \"healthy\" > healthy.html
                        wget https://wordpress.org/latest.tar.gz
                        tar -xzf latest.tar.gz
                        cp -r wordpress/* /var/www/html/
                        rm -rf wordpress
                        rm -rf latest.tar.gz
                        chmod -R 755 wp-content
                        chown -R apache:apache wp-content
                        echo -e 'Options +FollowSymlinks \nRewriteEngine on \nrewriterule ^wp-content/uploads/(.*)$ http://""",
                        GetAtt(cloudfront_distribution, 'DomainName'),
                        """/$1 [r=301,nc]' > .htaccess
                        chkconfig httpd on
                        cd /var/www
                        sudo chown -R apache /var/www/html
                        cd html/
                        sudo find . -type d -exec chmod 0755 {} \;
                        sudo find . -type f -exec chmod 0644 {} \;
                        sed -i 's/AllowOverride None/AllowOverride All/g' /etc/httpd/conf/httpd.conf
                        sed -i 's/AllowOverride none/AllowOverride All/g' /etc/httpd/conf/httpd.conf
                        echo -e "*/1 * * * * root aws s3 sync --delete s3://""",
                        Ref(bucket_wordpress_code),
                        """ /var/www/html">> /etc/crontab 
                        echo -e "*/1 * * * * root aws s3 sync --delete s3://""",
                        Ref(bucket_wordpress_media_assets),
                        """/var/www/html/wp-content/uploads">> /etc/crontab
                        service httpd start
                        """
                    ])
                )
            )
        )

        alb = template.add_resource(
            LoadBalancer(
                "{}ApplicationLoadBalancer".format(self.stage),
                Name="{}-wordpress-alb".format(self.stage),
                SecurityGroups=[Ref(web_dmz_security_group)],
                Subnets=public_subnets,
                Type="application"
            )
        )

        target_group = template.add_resource(
            TargetGroup(
                "{}TargetGroup".format(self.stage),
                Name="{}-wordpress-target-group".format(self.stage),
                Port=80,
                Protocol="HTTP",
                VpcId=ImportValue("{}{}VpcId".format(self.stage,vpc_name_formatted)),
                HealthCheckPort=8080
            )
        )

        template.add_resource(
            AutoScalingGroup(
                "{}AutoScalingGroup".format(self.stage),
                DependsOn="{}WordPressReadLaunchConfiguration".format(self.stage),
                AutoScalingGroupName="{}-wordpress-auto-scaling".format(self.stage),
                LaunchConfigurationName="{}-wordpress-launch-config".format(self.stage),
                TargetGroupARNs=[Ref(target_group)],
                MaxSize="3",
                MinSize="1",
                VPCZoneIdentifier=public_subnets,
                Tags=[
                    Tag("Name", "{}-wordpress-read-node".format(self.stage), True)
                ]
            )
        )

        template.add_resource(
            Listener(
                "ALBListener",
                DefaultActions=[
                    Action(
                        TargetGroupArn=Ref(target_group), 
                        Type="forward"
                    )
                ],
                LoadBalancerArn=Ref(alb),
                Port=80,
                Protocol="HTTP"
            )
        )

        f = open("modules/template_wordpress.yaml", 'w')
        print(template.to_yaml(), file=f)
    def _add_alb(self, cd, service_name, config, launch_type):
        sg_name = 'SG' + self.env + service_name
        svc_alb_sg = SecurityGroup(
            re.sub(r'\W+', '', sg_name),
            GroupName=self.env + '-' + service_name,
            SecurityGroupIngress=self._generate_alb_security_group_ingress(
                config
            ),
            VpcId=Ref(self.vpc),
            GroupDescription=Sub(service_name + "-alb-sg")
        )
        self.template.add_resource(svc_alb_sg)
        alb_name = service_name + pascalcase(self.env)
        if config['http_interface']['internal']:
            alb_subnets = [
                Ref(self.private_subnet1),
                Ref(self.private_subnet2)
            ]
            scheme = "internal"
            alb_name += 'Internal'
            alb_name = alb_name[:32]
            alb = ALBLoadBalancer(
                'ALB' + service_name,
                Subnets=alb_subnets,
                SecurityGroups=[
                    self.alb_security_group,
                    Ref(svc_alb_sg)
                ],
                Name=alb_name,
                Tags=[
                    {'Value': alb_name, 'Key': 'Name'}
                ],
                Scheme=scheme
            )
        else:
            alb_subnets = [
                Ref(self.public_subnet1),
                Ref(self.public_subnet2)
            ]
            alb_name = alb_name[:32]
            alb = ALBLoadBalancer(
                'ALB' + service_name,
                Subnets=alb_subnets,
                SecurityGroups=[
                    self.alb_security_group,
                    Ref(svc_alb_sg)
                ],
                Name=alb_name,
                Tags=[
                    {'Value': alb_name, 'Key': 'Name'}
                ]
            )

        self.template.add_resource(alb)

        target_group_name = "TargetGroup" + service_name
        health_check_path = config['http_interface']['health_check_path'] if 'health_check_path' in config['http_interface'] else "/elb-check"
        if config['http_interface']['internal']:
            target_group_name = target_group_name + 'Internal'

        target_group_config = {}
        if launch_type == self.LAUNCH_TYPE_FARGATE:
            target_group_config['TargetType'] = 'ip'

        service_target_group = TargetGroup(
            target_group_name,
            HealthCheckPath=health_check_path,
            HealthyThresholdCount=2,
            HealthCheckIntervalSeconds=30,
            TargetGroupAttributes=[
                TargetGroupAttribute(
                    Key='deregistration_delay.timeout_seconds',
                    Value='30'
                )
            ],
            VpcId=Ref(self.vpc),
            Protocol="HTTP",
            Matcher=Matcher(HttpCode="200-399"),
            Port=int(config['http_interface']['container_port']),
            HealthCheckTimeoutSeconds=10,
            UnhealthyThresholdCount=3,
            **target_group_config
        )

        self.template.add_resource(service_target_group)
        # Note: This is a ECS Loadbalancer definition. Not an ALB.
        # Defining this causes the target group to add a target to the correct
        # port in correct ECS cluster instance for the service container.
        lb = LoadBalancer(
            ContainerName=cd.Name,
            TargetGroupArn=Ref(service_target_group),
            ContainerPort=int(config['http_interface']['container_port'])
        )
        target_group_action = Action(
            TargetGroupArn=Ref(target_group_name),
            Type="forward"
        )
        service_listener = self._add_service_listener(
            service_name,
            target_group_action,
            alb,
            config['http_interface']['internal']
        )
        self._add_alb_alarms(service_name, alb)
        return alb, lb, service_listener, svc_alb_sg
Example #23
0
### Application Load Balancer Target Group ###

resources["ALBTargetGroup"] = template.add_resource(
    TargetGroup(
        "ALBTargetGroup",
        DependsOn=[resource for resource in ["ALB"]],
        HealthCheckIntervalSeconds=10,
        HealthCheckPath="/hello",
        HealthCheckProtocol="HTTP",
        HealthCheckTimeoutSeconds=5,
        HealthyThresholdCount=3,
        Name=Join("-", [Ref(parameters["Project"]), "tg"]),
        Port=80,
        Protocol="HTTP",
        Tags=[{
            "Key": "Project",
            "Value": Ref(parameters["Project"])
        }],
        TargetGroupAttributes=[
            TargetGroupAttribute(Key="deregistration_delay.timeout_seconds",
                                 Value="90"),
            TargetGroupAttribute(Key="stickiness.enabled", Value="true"),
            TargetGroupAttribute(Key="stickiness.type", Value="lb_cookie")
        ],
        TargetType="ip",
        UnhealthyThresholdCount=3,
        VpcId=ImportValue("VPC")))

### ECS Cluster ###

resources["HelloWorldECSCluster"] = template.add_resource(