def set_lb_definition(self): """ Function to parse the LB settings and properties and build the LB object :param ecs_composex.common.settings.ComposeXSettings settings: """ attrs = { "IpAddressType": "ipv4" if not keyisset("IpAddressType", self.properties) else self.properties["IpAddressType"], "Type": self.lb_type, "Scheme": "internet-facing" if self.lb_is_public else "internal", "SecurityGroups": [Ref(self.lb_sg)] if isinstance( self.lb_sg, SecurityGroup) else self.lb_sg, "Subnets": Ref(AWS_NO_VALUE), "SubnetMappings": Ref(AWS_NO_VALUE), "LoadBalancerAttributes": self.set_lb_attributes(), "Tags": Tags(Name=Sub(f"${{{ROOT_STACK_NAME.title}}}{self.logical_name}")), "Name": Ref(AWS_NO_VALUE), } self.lb = LoadBalancer(self.logical_name, **attrs) self.cfn_resource = self.lb
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 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_load_balancer(self): ''' Add load balancer to template ''' self.cfn_template.add_resource(LoadBalancer( title=constants.ALB, LoadBalancerAttributes=[LoadBalancerAttributes( Key='deletion_protection.enabled', Value='false' )], Scheme='internal', SecurityGroups=[Ref(constants.ALB_SG)], Subnets=[ ImportValue(Sub('${Environment}-${Subnet1}')), ImportValue(Sub('${Environment}-${Subnet2}')) ] )) return self.cfn_template
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 __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 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)
), SecurityGroupRule( IpProtocol="tcp", FromPort="22", ToPort="22", CidrIp='0.0.0.0/0', ) ])) load_balancer = template.add_resource( LoadBalancer('ApplicationElasticLB', Name=Join('-', ['api', 'elb', Ref(version)]), LoadBalancerAttributes=[ LoadBalancerAttributes(Key='access_logs.s3.enabled', Value='false'), LoadBalancerAttributes(Key='idle_timeout.timeout_seconds', 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,
def add_load_balancer(self, settings): """ Method to add LB to template :return: loadbalancer :rtype: troposphere.elasticloadbalancingv2.LoadBalancer """ if self.config.is_public and self.config.use_nlb(): self.add_public_ips(settings.aws_azs) no_value = Ref(AWS_NO_VALUE) public_mapping = define_public_mapping(self.eips, settings.aws_azs) if self.config.ingress_mappings and self.config.use_alb(): self.add_alb_sg(self.config.ingress_mappings.keys()) lb_sg = [Ref(self.alb_sg)] else: lb_sg = no_value loadbalancer = LoadBalancer( f"Microservice{self.config.lb_type.title()}LB", template=self.template, Scheme="internet-facing" if self.config.is_public else "internal", LoadBalancerAttributes=[ LoadBalancerAttributes(Key="load_balancing.cross_zone.enabled", Value="true") ] if self.config.lb_type == "network" else no_value, SecurityGroups=lb_sg, SubnetMappings=public_mapping if self.config.is_public and self.config.use_nlb() else no_value, Subnets=Ref(PUBLIC_SUBNETS) if self.config.is_public and self.config.use_alb() else Ref(vpc_params.APP_SUBNETS), Type=self.config.lb_type, Tags=Tags({ "Name": Sub(f"${{{SERVICE_NAME_T}}}-${{{ROOT_STACK_NAME_T}}}"), "StackName": Ref(AWS_STACK_NAME), "MicroserviceName": Ref(SERVICE_NAME), }), ) if self.config.is_public: sd_service = SdService( f"{self.resource_name}PublicDiscoveryService", template=self.template, Description=Ref(SERVICE_NAME), Condition=CREATE_PUBLIC_NAMESPACE_CON_T, NamespaceId=Ref(PUBLIC_DNS_ZONE_ID), HealthCheckCustomConfig=SdHealthCheckCustomConfig( FailureThreshold=1.0), DnsConfig=SdDnsConfig( RoutingPolicy="WEIGHTED", NamespaceId=Ref(AWS_NO_VALUE), DnsRecords=[SdDnsRecord(TTL="15", Type="A")], ), Name=If(USE_HOSTNAME_CON_T, Ref(SERVICE_HOSTNAME), Ref(SERVICE_NAME)), ) SdInstance( f"{self.resource_name}PublicLB", template=self.template, Condition=CREATE_PUBLIC_NAMESPACE_CON_T, ServiceId=GetAtt(sd_service, "Id"), InstanceAttributes={ "AWS_ALIAS_DNS_NAME": GetAtt(loadbalancer, "DNSName") }, ) if self.config.use_alb() and self.alb_sg: self.add_public_security_group_ingress(self.alb_sg) elif self.config.use_nlb(): self.add_public_security_group_ingress(SG_T) return loadbalancer
GroupDescription="Web load balancer security group.", VpcId=vpc, SecurityGroupIngress=[ SecurityGroupRule( IpProtocol="tcp", FromPort="443", ToPort="443", CidrIp='0.0.0.0/0', ), ], )) application_load_balancer = template.add_resource(LoadBalancer( 'ApplicationLoadBalancer', Subnets=[ loadbalancer_a_subnet, loadbalancer_b_subnet, ], SecurityGroups=[Ref(load_balancer_security_group)], )) application_listener = template.add_resource(Listener( 'ApplicationListener', Certificates=[elasticloadbalancingv2.Certificate( CertificateArn=acm_cluster_certificate_arn, )], LoadBalancerArn=Ref(application_load_balancer), Protocol='HTTPS', Port=443, DefaultActions=[Action( TargetGroupArn=Ref(application_target_group), Type='forward',
"Resource": Join("", ['s3:::', Ref(logs_bucket), "/alb/*"]), "Principal": { "AWS": Ref("AWS::AccountId") } }] }) template.add_resource(logs_bucket) template.add_resource(logs_bucket_policy) # 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',
# TODO: sort out ipv6 on vpc so can use dualstack here application_load_balancer = LoadBalancer( region.replace("-", "") + "ecsliveapplicationloadbalancer", IpAddressType = "ipv4", LoadBalancerAttributes = [ LoadBalancerAttributes( Key="access_logs.s3.enabled", Value = "true" ), LoadBalancerAttributes( Key="access_logs.s3.bucket", Value = "mgmt.eu-west-1.weblox.io" ), LoadBalancerAttributes( Key="access_logs.s3.prefix", Value = "logs" ) ], Scheme = "internet-facing", SecurityGroups = [ Ref(alb_security_group), Ref(security_group) ], Subnets = [ "subnet-0777c674d3018efd6", "subnet-0dec29b6660100d8d", "subnet-095d86cbe447af65e" ], Type = "application" )
LoadBalancerSG.title ], GroupId=Ref("LoadBalancerSG"), IpProtocol='tcp', FromPort='80', ToPort='80', DestinationSecurityGroupId=Ref("instanceSecurityGroup") )) LoadBalancer = t.add_resource(LoadBalancer( "LoadBalancer", Name="QAAppLoadBalancerTrop", Scheme="internet-facing", 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,
SecurityGroupIngress=[ SecurityGroupRule( IpProtocol="tcp", FromPort='0', ToPort='65535', CidrIp='0.0.0.0/0', ), ], ) application_load_balancer = LoadBalancer( 'ApplicationLoadBalancer', template=template, Subnets=[ Ref(loadbalancer_a_subnet), Ref(loadbalancer_b_subnet), ], SecurityGroups=[Ref(load_balancer_security_group)], ) template.add_output(Output( "LoadBalancerDNSName", Description="Loadbalancer DNS", Value=GetAtt(application_load_balancer, "DNSName") )) application_listener = Listener( 'ApplicationListener',
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 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 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)