def test_redirect_action(self): Action(Type='redirect', RedirectConfig=RedirectConfig( StatusCode='HTTP_301', Protocol='HTTPS', Host='api.troposphere.org', Path='redirect/#{path}')).to_dict()
def test_fixed_response_config_only_with_fixed_response(self): with self.assertRaises(ValueError): Action( Type="forward", FixedResponseConfig=FixedResponseConfig( ContentType="text/plain", ), ).to_dict()
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_redirect_config_only_with_redirect(self): with self.assertRaises(ValueError): Action( Type='forward', RedirectConfig=RedirectConfig( StatusCode='HTTP_301', ) ).to_dict()
def test_fixed_response_action(self): Action( Type="fixed-response", FixedResponseConfig=FixedResponseConfig( ContentType="text/plain", MessageBody="I am a fixed response", StatusCode="200", ), ).to_dict()
def test_fixed_response_action(self): Action( Type='fixed-response', FixedResponseConfig=FixedResponseConfig( ContentType='text/plain', MessageBody='I am a fixed response', StatusCode='200' ) ).to_dict()
def test_redirect_action(self): Action( Type="redirect", RedirectConfig=RedirectConfig( StatusCode="HTTP_301", Protocol="HTTPS", Host="api.troposphere.org", Path="redirect/#{path}", ), ).to_dict()
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 add_alb_listener(self): ''' Add an ALB listner ''' self.cfn_template.add_resource(Listener( title=constants.LISTENER, DefaultActions=[Action( TargetGroupArn=Ref(constants.ALB_TG), Type='forward')], LoadBalancerArn=Ref(constants.ALB), Port=int('8228'), Protocol='HTTP')) return self.cfn_template
def tea_pot(default_of_all=False) -> Action: """ Predefined reply for ALB config rule, returning HTTP Tea Pot """ return Action( FixedResponseConfig=FixedResponseConfig( ContentType="application/json", MessageBody=dumps({"Info": "Be our guest"}), StatusCode="418", ), Type="fixed-response", Order=Ref(AWS_NO_VALUE) if not default_of_all else 50000, )
def http_to_https_default(default_of_all=False) -> Action: """ Predefined rule to redirect HTTP to HTTPS """ return Action( RedirectConfig=RedirectConfig( Protocol="HTTPS", Port="443", Host="#{host}", Path="/#{path}", Query="#{query}", StatusCode=r"HTTP_301", ), Type="redirect", Order=Ref(AWS_NO_VALUE) if not default_of_all else 50000, )
def _add_service_listener(self, service_name, target_group_action, alb, internal): ssl_cert = Certificate(CertificateArn=self.ssl_certificate_arn) service_listener = Listener( "SslLoadBalancerListener" + service_name, Protocol="HTTPS", DefaultActions=[target_group_action], LoadBalancerArn=Ref(alb), Port=443, Certificates=[ssl_cert], SslPolicy="ELBSecurityPolicy-FS-1-2-Res-2019-08" ) self.template.add_resource(service_listener) if internal: # Allow HTTP traffic on internal services http_service_listener = Listener( "LoadBalancerListener" + service_name, Protocol="HTTP", DefaultActions=[target_group_action], LoadBalancerArn=Ref(alb), Port=80 ) self.template.add_resource(http_service_listener) else: # Redirect HTTP to HTTPS on external services redirection_config = RedirectConfig( StatusCode='HTTP_301', Protocol='HTTPS', Port='443' ) http_redirection_action = Action( RedirectConfig=redirection_config, Type="redirect" ) http_redirection_listener = Listener( "LoadBalancerRedirectionListener" + service_name, Protocol="HTTP", DefaultActions=[http_redirection_action], LoadBalancerArn=Ref(alb), Port=80 ) self.template.add_resource(http_redirection_listener) return service_listener
template.add_output(Output( "LoadBalancerDNSName", Description="Loadbalancer DNS", Value=GetAtt(application_load_balancer, "DNSName") )) application_listener = Listener( 'ApplicationListener', template=template, LoadBalancerArn=Ref(application_load_balancer), Protocol='HTTP', Port=8080, DefaultActions=[Action( TargetGroupArn=Ref(application_target_group), Type='forward', )] ) # ECS cluster cluster = Cluster( "Cluster", template=template, ) # ECS container role container_instance_role = iam.Role( "ContainerInstanceRole", template=template,
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
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( Field="path-pattern", Values=[ "/" ] ) ], Priority=1, ListenerArn="arn:aws:elasticloadbalancing:eu-west-1:837380460554:listener/app/live-euwes-14F0KA84VQMOV/95ccdb141691f73e/01a71f7b4d1f6a1d" ) template.add_resource(listener_rule)
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
def test_redirect_action_requires_redirect_config(self): with self.assertRaises(ValueError): Action(Type="redirect").to_dict()
def test_target_arn_only_forward(self): with self.assertRaises(ValueError): Action(Type="redirect", TargetGroupArn="").to_dict()
def test_forward_action_requires_target_arn(self): with self.assertRaises(ValueError): Action(Type="forward").to_dict()
def test_forward_action(self): Action( Type='forward', TargetGroupArn='' ).to_dict()
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())
def test_forward_action(self): Action(Type="forward", TargetGroupArn="").to_dict()
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=[ Action(Type="forward", TargetGroupArn=Ref(target_group)) ])) template.add_resource( ListenerRule( 'ListenerRule', ListenerArn=Ref(listener), Conditions=[Condition(Field='path-pattern', Values=['*'])], Actions=[Action(Type='forward', TargetGroupArn=Ref(target_group))], Priority=1000)) target_group_dimension = GetAtt(target_group, 'TargetGroupFullName') load_balancer_dimension = GetAtt(load_balancer, 'LoadBalancerFullName') template.add_resource( Alarm( 'LowHealthyHostsAlarm',
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)
], 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( "LaunchConfig", ImageId=image_id, IamInstanceProfile=Ref(example_instance_profile), InstanceType=instance_type, Metadata=Metadata( Init({ 'config': InitConfig( commands={
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
def test_fixed_response_requires_fixed_response_config(self): with self.assertRaises(ValueError): Action(Type="fixed-response").to_dict()
] ) template.add_resource(origin_certificate), template.add_resource(origin_issued_certificate), template.add_resource(certificate_dns_record) template.add_resource(alb_record_set_group) http_listener = Listener( region.replace("-", "") + "ecslivehttplistener", DefaultActions = [ Action( region.replace("-", "") + "ecslivehttpredirectaction", RedirectConfig = RedirectConfig( region.replace("-", "") + "ecslivehttpredirectconfig", Port = "443", Protocol = "HTTPS", StatusCode = "HTTP_301" ), Type = "redirect" ) ], LoadBalancerArn = Ref(application_load_balancer), Port = 80, Protocol = "HTTP" ) https_listener = Listener( region.replace("-", "") + "ecslivehttpslistener", Certificates = [ Certificate( CertificateArn = Ref(origin_issued_certificate)
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)) ])