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')
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
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), )
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), )
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) )
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), )
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), )
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)) ])
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)
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
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
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=[
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())
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(
# 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(
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) )]
"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
### 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(