Example #1
0
def get_instance_metadata(instance_name):
    return autoscaling.Metadata(
        cfn.Init({
            'config':
            cfn.InitConfig(
                packages={'yum': {
                    'httpd': []
                }},
                files=cfn.InitFiles({
                    '/etc/cfn/cfn-hup.conf':
                    cfn.InitFile(content=Join('', [
                        '[main]\n',
                        'stack=',
                        Ref('AWS::StackName'),
                        '\n',
                        'region=',
                        Ref('AWS::Region'),
                        '\n',
                    ]),
                                 mode='000400',
                                 owner='root',
                                 group='root'),
                    '/etc/cfn/hooks.d/cfn-auto-reloader.conf':
                    cfn.InitFile(content=Join('', [
                        '[cfn-auto-reloader-hook]\n',
                        'triggers=post.update\n',
                        'path=Resources.',
                        instance_name,
                        '.Metadata.AWS::CloudFormation::Init\n',
                        'action=/opt/aws/bin/cfn-init -v ',
                        '         --stack=',
                        Ref('AWS::StackName'),
                        '         --resource=',
                        instance_name,
                        '         --region=',
                        Ref('AWS::Region'),
                        '\n',
                        'runas=root\n',
                    ]))
                }),
                services={
                    'sysvinit':
                    cfn.InitServices({
                        'httpd':
                        cfn.InitService(enabled=True, ensureRunning=True),
                        'cfn-hup':
                        cfn.InitService(
                            enabled=True,
                            ensureRunning=True,
                            files=[
                                '/etc/cfn/cfn-hup.conf',
                                '/etc/cfn/hooks.d/cfn-auto-reloader.conf'
                            ])
                    })
                })
        }))
 Metadata=autoscaling.Metadata(
     cloudformation.Init({
         "config":
         cloudformation.InitConfig(
             files=cloudformation.InitFiles({
                 "/etc/rsyslog.d/20-somethin.conf":
                 cloudformation.InitFile(
                     source=Join(
                         "",
                         [
                             "http://",
                             Ref(DeployBucket),
                             ".s3.amazonaws.com/stacks/",
                             Ref(RootStackName),
                             "/env/etc/rsyslog.d/20-somethin.conf",
                         ],
                     ),
                     mode="000644",
                     owner="root",
                     group="root",
                     authentication="DeployUserAuth",
                 )
             }),
             services={
                 "sysvinit":
                 cloudformation.InitServices({
                     "rsyslog":
                     cloudformation.InitService(
                         enabled=True,
                         ensureRunning=True,
                         files=["/etc/rsyslog.d/20-somethin.conf"],
                     )
                 })
             },
         )
     }),
     cloudformation.Authentication({
         "DeployUserAuth":
         cloudformation.AuthenticationBlock(
             type="S3",
             accessKeyId=Ref(DeployUserAccessKey),
             secretKey=Ref(DeployUserSecretKey),
         )
     }),
 ),
Example #3
0
 Metadata=autoscaling.Metadata(
     cloudformation.Init(dict(
         config=cloudformation.InitConfig(
             commands=dict(
                 register_cluster=dict(command=Join("", [
                     "#!/bin/bash\n",
                     # Register the cluster
                     "echo ECS_CLUSTER=",
                     Ref(cluster),
                     " >> /etc/ecs/ecs.config\n",
                     # Enable CloudWatch docker logging
                     'echo \'ECS_AVAILABLE_LOGGING_DRIVERS=',
                     '["json-file","awslogs"]\'',
                     " >> /etc/ecs/ecs.config\n",
                     "yum update -y \n"
                 ]))
             ),
             files=cloudformation.InitFiles({
                 "/etc/cfn/cfn-hup.conf": cloudformation.InitFile(
                     content=Join("", [
                         "[main]\n",
                         "stack=",
                         Ref(AWS_STACK_ID),
                         "\n",
                         "region=",
                         Ref(AWS_REGION),
                         "\n",
                     ]),
                     mode="000400",
                     owner="root",
                     group="root",
                 ),
                 "/etc/cfn/hooks.d/cfn-auto-reloader.conf":
                 cloudformation.InitFile(
                     content=Join("", [
                         "[cfn-auto-reloader-hook]\n",
                         "triggers=post.update\n",
                         "path=Resources.%s."
                         % container_instance_configuration_name,
                         "Metadata.AWS::CloudFormation::Init\n",
                         "action=/opt/aws/bin/cfn-init -v ",
                         "         --stack ",
                         Ref(AWS_STACK_NAME),
                         "         --resource %s"
                         % container_instance_configuration_name,
                         "         --region ",
                         Ref("AWS::Region"),
                         "\n",
                         "runas=root\n",
                     ])
                 )
             }),
             services=dict(
                 sysvinit=cloudformation.InitServices({
                     'cfn-hup': cloudformation.InitService(
                         enabled=True,
                         ensureRunning=True,
                         files=[
                             "/etc/cfn/cfn-hup.conf",
                             "/etc/cfn/hooks.d/cfn-auto-reloader.conf",
                         ]
                     ),
                 })
             )
         )
     ))
 ),
Example #4
0
def buildStack(bootstrap, env):
    t = Template()

    t.add_description("""\
    Configures autoscaling group for hello world app""")

    vpcCidr = t.add_parameter(
        Parameter(
            "VPCCidr",
            Type="String",
            Description="VPC cidr (x.x.x.x/xx)",
        ))

    publicSubnet1 = t.add_parameter(
        Parameter(
            "PublicSubnet1",
            Type="String",
            Description="A public VPC subnet ID for the api app load balancer.",
        ))

    publicSubnet2 = t.add_parameter(
        Parameter(
            "PublicSubnet2",
            Type="String",
            Description="A public VPC subnet ID for the api load balancer.",
        ))

    dbName = t.add_parameter(
        Parameter(
            "DBName",
            Default="HelloWorldApp",
            Description="The database name",
            Type="String",
            MinLength="1",
            MaxLength="64",
            AllowedPattern="[a-zA-Z][a-zA-Z0-9]*",
            ConstraintDescription=("must begin with a letter and contain only"
                                   " alphanumeric characters.")))

    dbUser = t.add_parameter(
        Parameter(
            "DBUser",
            NoEcho=True,
            Description="The database admin account username",
            Type="String",
            MinLength="1",
            MaxLength="16",
            AllowedPattern="[a-zA-Z][a-zA-Z0-9]*",
            ConstraintDescription=("must begin with a letter and contain only"
                                   " alphanumeric characters.")))

    dbPassword = t.add_parameter(
        Parameter(
            "DBPassword",
            NoEcho=True,
            Description="The database admin account password",
            Type="String",
            MinLength="8",
            MaxLength="41",
            AllowedPattern="[a-zA-Z0-9]*",
            ConstraintDescription="must contain only alphanumeric characters.")
    )

    dbType = t.add_parameter(
        Parameter(
            "DBType",
            Default="db.t2.medium",
            Description="Database instance class",
            Type="String",
            AllowedValues=[
                "db.m5.large", "db.m5.xlarge", "db.m5.2xlarge",
                "db.m5.4xlarge", "db.m5.12xlarge", "db.m5.24xlarge",
                "db.m4.large", "db.m4.xlarge", "db.m4.2xlarge",
                "db.m4.4xlarge", "db.m4.10xlarge", "db.m4.16xlarge",
                "db.r4.large", "db.r4.xlarge", "db.r4.2xlarge",
                "db.r4.4xlarge", "db.r4.8xlarge", "db.r4.16xlarge",
                "db.x1e.xlarge", "db.x1e.2xlarge", "db.x1e.4xlarge",
                "db.x1e.8xlarge", "db.x1e.16xlarge", "db.x1e.32xlarge",
                "db.x1.16xlarge", "db.x1.32xlarge", "db.r3.large",
                "db.r3.xlarge", "db.r3.2xlarge", "db.r3.4xlarge",
                "db.r3.8xlarge", "db.t2.micro", "db.t2.small", "db.t2.medium",
                "db.t2.large", "db.t2.xlarge", "db.t2.2xlarge"
            ],
            ConstraintDescription="must select a valid database instance type.",
        ))

    dbAllocatedStorage = t.add_parameter(
        Parameter(
            "DBAllocatedStorage",
            Default="5",
            Description="The size of the database (Gb)",
            Type="Number",
            MinValue="5",
            MaxValue="1024",
            ConstraintDescription="must be between 5 and 1024Gb.",
        ))

    whitelistedCIDR = t.add_parameter(
        Parameter(
            "WhitelistedCIDR",
            Description="CIDR whitelisted to be open on public instances",
            Type="String",
        ))

    #### NETWORK SECTION ####
    vpc = t.add_resource(
        VPC("VPC", CidrBlock=Ref(vpcCidr), EnableDnsHostnames=True))

    subnet1 = t.add_resource(
        Subnet("Subnet1",
               CidrBlock=Ref(publicSubnet1),
               AvailabilityZone="eu-west-1a",
               VpcId=Ref(vpc)))
    subnet2 = t.add_resource(
        Subnet("Subnet2",
               CidrBlock=Ref(publicSubnet2),
               AvailabilityZone="eu-west-1b",
               VpcId=Ref(vpc)))

    internetGateway = t.add_resource(InternetGateway('InternetGateway'))

    gatewayAttachment = t.add_resource(
        VPCGatewayAttachment('AttachGateway',
                             VpcId=Ref(vpc),
                             InternetGatewayId=Ref(internetGateway)))

    routeTable = t.add_resource(RouteTable('RouteTable', VpcId=Ref(vpc)))

    route = t.add_resource(
        Route(
            'Route',
            DependsOn='AttachGateway',
            GatewayId=Ref('InternetGateway'),
            DestinationCidrBlock='0.0.0.0/0',
            RouteTableId=Ref(routeTable),
        ))

    subnetRouteTableAssociation = t.add_resource(
        SubnetRouteTableAssociation(
            'SubnetRouteTableAssociation',
            SubnetId=Ref(subnet1),
            RouteTableId=Ref(routeTable),
        ))

    subnetRouteTableAssociation2 = t.add_resource(
        SubnetRouteTableAssociation(
            'SubnetRouteTableAssociation2',
            SubnetId=Ref(subnet2),
            RouteTableId=Ref(routeTable),
        ))

    #### SECURITY GROUP ####
    loadBalancerSg = t.add_resource(
        ec2.SecurityGroup(
            "LoadBalancerSecurityGroup",
            VpcId=Ref(vpc),
            GroupDescription="Enable SSH access via port 22",
            SecurityGroupIngress=[
                ec2.SecurityGroupRule(
                    IpProtocol="tcp",
                    FromPort="80",
                    ToPort="80",
                    CidrIp="0.0.0.0/0",
                ),
            ],
        ))

    instanceSg = t.add_resource(
        ec2.SecurityGroup(
            "InstanceSecurityGroup",
            VpcId=Ref(vpc),
            GroupDescription="Enable SSH access via port 22",
            SecurityGroupIngress=[
                ec2.SecurityGroupRule(
                    IpProtocol="tcp",
                    FromPort="22",
                    ToPort="22",
                    CidrIp=Ref(whitelistedCIDR),
                ),
                ec2.SecurityGroupRule(
                    IpProtocol="tcp",
                    FromPort="8000",
                    ToPort="8000",
                    SourceSecurityGroupId=Ref(loadBalancerSg),
                ),
            ],
        ))

    rdsSg = t.add_resource(
        SecurityGroup("RDSSecurityGroup",
                      GroupDescription="Security group for RDS DB Instance.",
                      VpcId=Ref(vpc),
                      SecurityGroupIngress=[
                          ec2.SecurityGroupRule(
                              IpProtocol="tcp",
                              FromPort="5432",
                              ToPort="5432",
                              SourceSecurityGroupId=Ref(instanceSg),
                          ),
                          ec2.SecurityGroupRule(
                              IpProtocol="tcp",
                              FromPort="5432",
                              ToPort="5432",
                              CidrIp=Ref(whitelistedCIDR),
                          ),
                      ]))

    #### DATABASE SECTION ####
    subnetGroup = t.add_resource(
        DBSubnetGroup(
            "SubnetGroup",
            DBSubnetGroupDescription=
            "Subnets available for the RDS DB Instance",
            SubnetIds=[Ref(subnet1), Ref(subnet2)],
        ))

    db = t.add_resource(
        DBInstance(
            "RDSHelloWorldApp",
            DBName=Join("", [Ref(dbName), env]),
            DBInstanceIdentifier=Join("", [Ref(dbName), env]),
            EnableIAMDatabaseAuthentication=True,
            PubliclyAccessible=True,
            AllocatedStorage=Ref(dbAllocatedStorage),
            DBInstanceClass=Ref(dbType),
            Engine="postgres",
            EngineVersion="10.4",
            MasterUsername=Ref(dbUser),
            MasterUserPassword=Ref(dbPassword),
            DBSubnetGroupName=Ref(subnetGroup),
            VPCSecurityGroups=[Ref(rdsSg)],
        ))

    t.add_output(
        Output("RDSConnectionString",
               Description="Connection string for database",
               Value=GetAtt("RDSHelloWorldApp", "Endpoint.Address")))

    if (bootstrap):
        return t

    #### INSTANCE SECTION ####
    keyName = t.add_parameter(
        Parameter(
            "KeyName",
            Type="String",
            Description="Name of an existing EC2 KeyPair to enable SSH access",
            MinLength="1",
            AllowedPattern="[\x20-\x7E]*",
            MaxLength="255",
            ConstraintDescription="can contain only ASCII characters.",
        ))

    scaleCapacityMin = t.add_parameter(
        Parameter(
            "ScaleCapacityMin",
            Default="1",
            Type="String",
            Description="Number of api servers to run",
        ))

    scaleCapacityMax = t.add_parameter(
        Parameter(
            "ScaleCapacityMax",
            Default="1",
            Type="String",
            Description="Number of api servers to run",
        ))

    scaleCapacityDesired = t.add_parameter(
        Parameter(
            "ScaleCapacityDesired",
            Default="1",
            Type="String",
            Description="Number of api servers to run",
        ))

    amiId = t.add_parameter(
        Parameter(
            "AmiId",
            Type="String",
            Default="ami-09693313102a30b2c",
            Description="The AMI id for the api instances",
        ))

    instanceType = t.add_parameter(
        Parameter("InstanceType",
                  Description="WebServer EC2 instance type",
                  Type="String",
                  Default="t2.medium",
                  AllowedValues=[
                      "t2.nano", "t2.micro", "t2.small", "t2.medium",
                      "t2.large", "m3.medium", "m3.large", "m3.xlarge",
                      "m3.2xlarge", "m4.large", "m4.xlarge", "m4.2xlarge",
                      "m4.4xlarge", "m4.10xlarge", "c4.large", "c4.xlarge",
                      "c4.2xlarge", "c4.4xlarge", "c4.8xlarge"
                  ],
                  ConstraintDescription="must be a valid EC2 instance type."))

    assumeRole = t.add_resource(
        Role("AssumeRole",
             AssumeRolePolicyDocument=json.loads("""\
{
  "Version": "2012-10-17",
  "Statement": [
    {
    "Action": "sts:AssumeRole",
    "Principal": {
      "Service": "ec2.amazonaws.com"
    },
    "Effect": "Allow",
    "Sid": ""
    }
  ]
}\
""")))

    instanceProfile = t.add_resource(
        InstanceProfile("InstanceProfile", Roles=[Ref(assumeRole)]))

    rolePolicyType = t.add_resource(
        PolicyType("RolePolicyType",
                   Roles=[Ref(assumeRole)],
                   PolicyName=Join("", ["CloudWatchHelloWorld", "-", env]),
                   PolicyDocument=json.loads("""\
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": [
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:DescribeLogStreams",
        "logs:PutLogEvents"
      ],
    "Effect": "Allow",
    "Resource": [
        "arn:aws:logs:*:*:*"
      ]
    }
  ]
}\
""")))

    appPassword = t.add_parameter(
        Parameter(
            "AppPassword",
            NoEcho=True,
            Description="The Password for the app user",
            Type="String",
            MinLength="8",
            MaxLength="41",
            AllowedPattern="[a-zA-Z0-9]*",
            ConstraintDescription="must contain only alphanumeric characters.")
    )

    launchConfig = t.add_resource(
        LaunchConfiguration(
            "LaunchConfiguration",
            Metadata=autoscaling.Metadata(
                cloudformation.Init({
                    "config":
                    cloudformation.InitConfig(files=cloudformation.InitFiles({
                        "/home/app/environment":
                        cloudformation.InitFile(content=Join(
                            "", [
                                "SPRING_DATASOURCE_URL=", "jdbc:postgresql://",
                                GetAtt("RDSHelloWorldApp", "Endpoint.Address"),
                                ":5432/HelloWorldApp" + env +
                                "?currentSchema=hello_world", "\n",
                                "SPRING_DATASOURCE_USERNAME=app", "\n",
                                "SPRING_DATASOURCE_PASSWORD="******"\n",
                                "SPRING_PROFILES_ACTIVE=", env, "\n"
                            ]),
                                                mode="000600",
                                                owner="app",
                                                group="app")
                    }), )
                }), ),
            UserData=Base64(
                Join('', [
                    "#!/bin/bash\n", "/opt/aws/bin/cfn-init",
                    "    --resource LaunchConfiguration", "    --stack ",
                    Ref("AWS::StackName"), "    --region ",
                    Ref("AWS::Region"), "\n", "/opt/aws/bin/cfn-signal -e $? ",
                    "         --stack ", {
                        "Ref": "AWS::StackName"
                    }, "         --resource AutoscalingGroup ",
                    "         --region ", {
                        "Ref": "AWS::Region"
                    }, "\n"
                ])),
            ImageId=Ref(amiId),
            KeyName=Ref(keyName),
            IamInstanceProfile=Ref(instanceProfile),
            BlockDeviceMappings=[
                ec2.BlockDeviceMapping(DeviceName="/dev/xvda",
                                       Ebs=ec2.EBSBlockDevice(VolumeSize="8")),
            ],
            SecurityGroups=[Ref(instanceSg)],
            InstanceType=Ref(instanceType),
            AssociatePublicIpAddress='True',
        ))

    applicationElasticLB = t.add_resource(
        elb.LoadBalancer("ApplicationElasticLB",
                         Name="ApplicationElasticLB-" + env,
                         Scheme="internet-facing",
                         Type="application",
                         SecurityGroups=[Ref(loadBalancerSg)],
                         Subnets=[Ref(subnet1), Ref(subnet2)]))

    targetGroup = t.add_resource(
        elb.TargetGroup("TargetGroupHelloWorld",
                        HealthCheckProtocol="HTTP",
                        HealthCheckTimeoutSeconds="15",
                        HealthyThresholdCount="5",
                        Matcher=elb.Matcher(HttpCode="200,404"),
                        Port="8000",
                        Protocol="HTTP",
                        UnhealthyThresholdCount="3",
                        TargetGroupAttributes=[
                            elb.TargetGroupAttribute(
                                Key="deregistration_delay.timeout_seconds",
                                Value="120",
                            )
                        ],
                        VpcId=Ref(vpc)))

    listener = t.add_resource(
        elb.Listener("Listener",
                     Port="80",
                     Protocol="HTTP",
                     LoadBalancerArn=Ref(applicationElasticLB),
                     DefaultActions=[
                         elb.Action(Type="forward",
                                    TargetGroupArn=Ref(targetGroup))
                     ]))

    t.add_output(
        Output("URL",
               Description="URL of the sample website",
               Value=Join("",
                          ["http://",
                           GetAtt(applicationElasticLB, "DNSName")])))

    autoScalingGroup = t.add_resource(
        AutoScalingGroup(
            "AutoscalingGroup",
            DesiredCapacity=Ref(scaleCapacityDesired),
            LaunchConfigurationName=Ref(launchConfig),
            MinSize=Ref(scaleCapacityMin),
            MaxSize=Ref(scaleCapacityMax),
            VPCZoneIdentifier=[Ref(subnet1), Ref(subnet2)],
            TargetGroupARNs=[Ref(targetGroup)],
            HealthCheckType="ELB",
            HealthCheckGracePeriod=360,
            UpdatePolicy=UpdatePolicy(
                AutoScalingReplacingUpdate=AutoScalingReplacingUpdate(
                    WillReplace=True, ),
                AutoScalingRollingUpdate=AutoScalingRollingUpdate(
                    PauseTime='PT5M',
                    MinInstancesInService="1",
                    MaxBatchSize='1',
                    WaitOnResourceSignals=True)),
            CreationPolicy=CreationPolicy(ResourceSignal=ResourceSignal(
                Timeout="PT15M", Count=Ref(scaleCapacityDesired)))))

    # print(t.to_json())
    return t
Example #5
0
 def _launch_config(self):
     return LaunchConfiguration(
         "LaunchConfiguration",
         Metadata=autoscaling.Metadata(
             cloudformation.Init({
                 "config":
                 cloudformation.InitConfig(files=cloudformation.InitFiles({
                     '/etc/cfn/cfn-hup.conf':
                     cloudformation.InitFile(content=Join(
                         '', [
                             '[main]\n',
                             'stack=',
                             self.ref_stack_id,
                             '\n',
                             'region=',
                             self.ref_region,
                             '\n',
                         ]),
                                             mode='000400',
                                             owner='root',
                                             group='root'),
                     '/etc/cfn/hooks.d/cfn-auto-reloader.conf':
                     cloudformation.InitFile(
                         content=Join('', [
                             '[cfn-auto-reloader-hook]\n',
                             'triggers=post.update\n',
                             'path=Resources.WebServerInstance.\
                 Metadata.AWS::CloudFormation::Init\n',
                             'action=/opt/aws/bin/cfn-init -v ',
                             '         --stack ',
                             self.ref_stack_name,
                             '         --resource WebServerInstance ',
                             '         --region ',
                             self.ref_region,
                             '\n',
                             'runas=root\n',
                         ]))
                 }),
                                           services={
                                               "sysvinit":
                                               cloudformation.InitServices({
                                                   "rsyslog":
                                                   cloudformation.
                                                   InitService(
                                                       enabled=True,
                                                       ensureRunning=True,
                                                       files=[
                                                           '/etc/rsyslog.d/20-somethin.conf'
                                                       ])
                                               })
                                           })
             })),
         UserData=Base64(Join('', self.config['app_instance_user_data'])),
         ImageId=FindInMap("RegionMap", Ref("AWS::Region"), "AMI"),
         KeyName=self.config['sshkey'],
         IamInstanceProfile=Ref(self.instance_iam_role_instance_profile),
         BlockDeviceMappings=[
             ec2.BlockDeviceMapping(DeviceName=self.config['device_name'],
                                    Ebs=ec2.EBSBlockDevice(VolumeSize="8")),
         ],
         SecurityGroups=self.config['app_sg'],
         InstanceType=self.config['instance_type'],
     )
    def init_template(self):
        self.template.add_description(self.TEMPLATE_DESCRIPTION)

        ecs_cluster = self.template.add_resource(Cluster(self.CLUSTER_NAME))

        ecs_instance_role = self.template.add_resource(
            Role('sitInstanceRole',
                 Path='/',
                 AssumeRolePolicyDocument={
                     "Statement": [{
                         "Effect": "Allow",
                         "Principal": {
                             "Service": ["ec2.amazonaws.com"]
                         },
                         "Action": ["sts:AssumeRole"]
                     }]
                 }))

        ecs_instance_profile = self.template.add_resource(
            InstanceProfile('sitInstanceProfile',
                            Path='/',
                            Roles=[Ref(ecs_instance_role)]))

        ecs_instance_policy = self.template.add_resource(
            PolicyType('sitInstancePolicy',
                       PolicyName='ecs-policy',
                       Roles=[Ref(ecs_instance_role)],
                       PolicyDocument={
                           "Statement": [{
                               "Effect":
                               "Allow",
                               "Action": [
                                   "ecs:CreateCluster",
                                   "ecs:RegisterContainerInstance",
                                   "ecs:DeregisterContainerInstance",
                                   "ecs:DiscoverPollEndpoint", "ecs:Submit*",
                                   "ecs:Poll", "ecs:StartTelemetrySession",
                                   "ecr:GetAuthorizationToken",
                                   "ecr:BatchCheckLayerAvailability",
                                   "ecr:GetDownloadUrlForLayer",
                                   "ecr:BatchGetImage", "logs:CreateLogStream",
                                   "logs:PutLogEvents"
                               ],
                               "Resource":
                               "*"
                           }],
                       }))

        commands = {
            '01_add_instance_to_cluster': {
                'command':
                Join('', [
                    '#!/bin/bash\n', 'echo ECS_CLUSTER=',
                    Ref(ecs_cluster),
                    '$"\n"ECS_ENGINE_TASK_CLEANUP_WAIT_DURATION=',
                    self.ECS_TASK_CLEANUP_WAIT, ' >> /etc/ecs/ecs.config'
                ])
            }
        }

        files = {
            "/etc/cfn/cfn-hup.conf": {
                "content":
                Join("", [
                    "[main]\n", "stack=",
                    Ref("AWS::StackId"), "\n", "region=",
                    Ref("AWS::Region"), "\n"
                ]),
                "mode":
                "000400",
                "owner":
                "root",
                "group":
                "root"
            },
            "/etc/cfn/hooks.d/cfn-auto-reloader.conf": {
                "content":
                Join("", [
                    "[cfn-auto-reloader-hook]\n", "triggers=post.update\n",
                    "path=Resources.{0}.Metadata.AWS::CloudFormation::Init\n".
                    format(self.LAUNCH_CONFIGURATION_NAME),
                    "action=/opt/aws/bin/cfn-init -v ", "         --stack ",
                    Ref("AWS::StackName"), "         --resource {0}".format(
                        self.LAUNCH_CONFIGURATION_NAME), "         --region ",
                    Ref("AWS::Region"), "\n", "runas=root\n"
                ])
            }
        }

        services = {
            "sysvinit": {
                "cfn-hup": {
                    "enabled":
                    "true",
                    "ensureRunning":
                    "true",
                    "files": [
                        "/etc/cfn/cfn-hup.conf",
                        "/etc/cfn/hooks.d/cfn-auto-reloader.conf"
                    ]
                }
            }
        }

        launch_configuration = self.template.add_resource(
            LaunchConfiguration(self.LAUNCH_CONFIGURATION_NAME,
                                ImageId=self.AMI_ID,
                                IamInstanceProfile=Ref(ecs_instance_profile),
                                InstanceType=self.INSTANCE_TYPE,
                                UserData=self.user_data.get_base64_data(),
                                AssociatePublicIpAddress=False,
                                SecurityGroups=self.SECURITY_GROUPS,
                                KeyName=self.KEY_NAME,
                                Metadata=autoscaling.Metadata(
                                    cloudformation.Init({
                                        "config":
                                        cloudformation.InitConfig(
                                            commands=commands,
                                            files=files,
                                            services=services)
                                    })),
                                BlockDeviceMappings=[
                                    autoscaling.BlockDeviceMapping(
                                        DeviceName=self.EBS_DEVICE_NAME,
                                        Ebs=autoscaling.EBSBlockDevice(
                                            DeleteOnTermination=True,
                                            VolumeSize=self.EBS_VOLUME_SIZE,
                                            VolumeType='gp2'))
                                ]))

        auto_scaling_group = self.template.add_resource(
            AutoScalingGroup(self.AUTOSCALING_GROUP_NAME,
                             MaxSize=self.MAX_SIZE,
                             MinSize=self.MIN_SIZE,
                             Cooldown=60,
                             LaunchConfigurationName=Ref(launch_configuration),
                             VPCZoneIdentifier=[self.SUBNET]))
        """ Scale UP Policy """
        scaling_up_policy = self.template.add_resource(
            ScalingPolicy('{0}ScaleUpPolicy'.format(
                self.AUTOSCALING_GROUP_NAME),
                          AdjustmentType='ChangeInCapacity',
                          AutoScalingGroupName=Ref(auto_scaling_group),
                          Cooldown=60,
                          ScalingAdjustment='1'))

        for alarm_name, alarm in self.AUTOSCALE_UP_ALARMS.iteritems():
            """ Cloud Watch Alarm """
            self.template.add_resource(
                Alarm('{0}ScaleUp{1}'.format(self.AUTOSCALING_GROUP_NAME,
                                             alarm_name),
                      ActionsEnabled=True,
                      Namespace='AWS/ECS',
                      MetricName=alarm['scaling_metric'],
                      ComparisonOperator='GreaterThanOrEqualToThreshold',
                      Threshold=alarm['scale_up_threshold'],
                      EvaluationPeriods=1,
                      Statistic=alarm['statistic'],
                      Period=alarm['period'],
                      AlarmActions=[Ref(scaling_up_policy)],
                      Dimensions=[
                          MetricDimension(Name='ClusterName',
                                          Value=Ref(ecs_cluster))
                      ]))
        """ Scale DOWN Policy """
        scaling_down_policy = self.template.add_resource(
            ScalingPolicy('{0}ScaleDownPolicy'.format(
                self.AUTOSCALING_GROUP_NAME),
                          AdjustmentType='ChangeInCapacity',
                          AutoScalingGroupName=Ref(auto_scaling_group),
                          Cooldown=60,
                          ScalingAdjustment='-1'))

        for alarm_name, alarm in self.AUTOSCALE_DOWN_ALARMS.iteritems():
            """ Cloud Watch Alarm """
            self.template.add_resource(
                Alarm('{0}ScaleDown{1}'.format(self.AUTOSCALING_GROUP_NAME,
                                               alarm_name),
                      ActionsEnabled=True,
                      Namespace='AWS/ECS',
                      MetricName=alarm['scaling_metric'],
                      ComparisonOperator='LessThanOrEqualToThreshold',
                      Threshold=alarm['scale_down_threshold'],
                      EvaluationPeriods=1,
                      Statistic=alarm['statistic'],
                      Period=alarm['period'],
                      AlarmActions=[Ref(scaling_down_policy)],
                      Dimensions=[
                          MetricDimension(Name='ClusterName',
                                          Value=Ref(ecs_cluster))
                      ]))
def template():

    t = Template()

    keyname_param = t.add_parameter(
        Parameter(
            "KeyName",
            Description=
            "Name of an existing EC2 KeyPair to enable SSH access to the instance",
            Type="String"))

    image_id_param = t.add_parameter(
        Parameter("ImageId",
                  Description="ImageId of the EC2 instance",
                  Type="String"))

    instance_type_param = t.add_parameter(
        Parameter("InstanceType",
                  Description="Type of the EC2 instance",
                  Type="String"))

    ScaleCapacity = t.add_parameter(
        Parameter(
            "ScaleCapacity",
            Default="1",
            Type="String",
            Description="Number of api servers to run",
        ))

    VPCAvailabilityZone2 = t.add_parameter(
        Parameter(
            "VPCAvailabilityZone2",
            MinLength="1",
            Type="String",
            Description="Second availability zone",
            MaxLength="255",
        ))

    VPCAvailabilityZone1 = t.add_parameter(
        Parameter(
            "VPCAvailabilityZone1",
            MinLength="1",
            Type="String",
            Description="First availability zone",
            MaxLength="255",
        ))

    SecurityGroup = t.add_parameter(
        Parameter(
            "SecurityGroup",
            Type="String",
            Description="Security group.",
        ))

    RootStackName = t.add_parameter(
        Parameter(
            "RootStackName",
            Type="String",
            Description="The root stack name",
        ))

    ApiSubnet2 = t.add_parameter(
        Parameter(
            "ApiSubnet2",
            Type="String",
            Description="Second private VPC subnet ID for the api app.",
        ))

    ApiSubnet1 = t.add_parameter(
        Parameter(
            "ApiSubnet1",
            Type="String",
            Description="First private VPC subnet ID for the api app.",
        ))

    #####################################################
    # Launch Configuration
    #####################################################
    LaunchConfig = t.add_resource(
        LaunchConfiguration(
            "LaunchConfiguration",
            Metadata=autoscaling.Metadata(
                cloudformation.Init(
                    cloudformation.InitConfigSets(InstallAndRun=['Install']),
                    Install=cloudformation.InitConfig(
                        packages={
                            "apt": {
                                "curl": [],
                                "zip": [],
                                "unzip": [],
                                "git": [],
                                "supervisor": [],
                                "sqlite3": [],
                                "nginx": [],
                                "php7.2-fpm": [],
                                "php7.2-cli": [],
                                "php7.2-pgsql": [],
                                "php7.2-sqlite3": [],
                                "php7.2-gd": [],
                                "php7.2-curl": [],
                                "php7.2-memcached": [],
                                "php7.2-imap": [],
                                "php7.2-mysql": [],
                                "php7.2-mbstring": [],
                                "php7.2-xml": [],
                                "php7.2-zip": [],
                                "php7.2-bcmath": [],
                                "php7.2-soap": [],
                                "php7.2-intl": [],
                                "php7.2-readline": [],
                                "php-msgpack": [],
                                "php-igbinary": []
                            }
                        },
                        files=cloudformation.InitFiles({
                            "/etc/nginx/sites-available/default":
                            cloudformation.
                            InitFile(content=Join('', [
                                "server {\n", "   listen 80 default_server;\n",
                                "   root /var/www/html/public;\n",
                                "   index index.html index.htm index.php;\n",
                                "   server_name _;\n", "   charset utf-8;\n",
                                "   location = /favicon.ico { log_not_found off; access_log off; }\n",
                                "   location = /robots.txt  { log_not_found off; access_log off; }\n",
                                "   location / {\n",
                                "       try_files $uri $uri/ /index.php$is_args$args;\n",
                                "   }\n", "   location ~ \.php$ {\n",
                                "       include snippets/fastcgi-php.conf;\n",
                                "       fastcgi_pass unix:/run/php/php7.2-fpm.sock;\n",
                                "   }\n", "   error_page 404 /index.php;\n",
                                "}\n"
                            ])),
                            "/etc/supervisor/conf.d/supervisord.conf":
                            cloudformation.
                            InitFile(content=Join('', [
                                "[supervisord]\n",
                                "nodaemon=true\n",
                                "[program:nginx]\n",
                                "command=nginx\n",
                                "stdout_logfile=/dev/stdout\n",
                                "stdout_logfile_maxbytes=0\n",
                                "stderr_logfile=/dev/stderr\n",
                                "stderr_logfile_maxbytes=0\n",
                                "[program:php-fpm]\n",
                                "command=php-fpm7.2\n",
                                "stdout_logfile=/dev/stdout\n",
                                "stdout_logfile_maxbytes=0\n",
                                "stderr_logfile=/dev/stderr\n",
                                "stderr_logfile_maxbytes=0\n",
                                "[program:horizon]\n",
                                "process_name=%(program_name)s\n",
                                "command=php /var/www/html/artisan horizon\n",
                                "autostart=true\n",
                                "autorestart=true\n",
                                "user=root\n",
                                "redirect_stderr=true\n",
                                "stdout_logfile=/var/www/html/storage/logs/horizon.log\n",
                            ])),
                            "/etc/php/7.2/fpm/php-fpm.conf":
                            cloudformation.InitFile(
                                content=Join('', [
                                    "[global]\n",
                                    "pid = /run/php/php7.2-fpm.pid\n",
                                    "error_log = /proc/self/fd/2\n",
                                    "include=/etc/php/7.2/fpm/pool.d/*.conf\n"
                                ]))
                        }))), ),
            UserData=Base64(
                Join('', [
                    "#!/bin/bash -xe\n", "apt-get update -y\n",
                    "apt-get install -y language-pack-en-base\n",
                    "export LC_ALL=en_US.UTF-8\n", "export LANG=en_US.UTF-8\n",
                    "apt-get install -y ruby\n",
                    "wget https://aws-codedeploy-ap-south-1.s3.amazonaws.com/latest/install\n",
                    "chmod +x ./install\n", "./install auto\n",
                    "service codedeploy-agent start\n",
                    "apt-get install -y software-properties-common python-software-properties\n",
                    "add-apt-repository -y ppa:ondrej/php\n",
                    "apt-get update -y\n",
                    "apt-get install -y python-setuptools\n",
                    "mkdir -p /opt/aws/bin\n",
                    "wget https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-latest.tar.gz\n",
                    "easy_install --script-dir /opt/aws/bin aws-cfn-bootstrap-latest.tar.gz\n",
                    "# Install the files and packages from the metadata\n",
                    "/opt/aws/bin/cfn-init -v ", " --stack ",
                    Ref("AWS::StackName"), " --resource LaunchConfiguration",
                    " --configsets InstallAndRun ", " --region ",
                    Ref("AWS::Region"), "\n"
                ])),
            ImageId=Ref("ImageId"),
            KeyName=Ref(keyname_param),
            BlockDeviceMappings=[
                ec2.BlockDeviceMapping(DeviceName="/dev/sda1",
                                       Ebs=ec2.EBSBlockDevice(VolumeSize="8")),
            ],
            InstanceType=Ref("InstanceType"),
            IamInstanceProfile="CodeDeployDemo-EC2-Instance-Profile",
            SecurityGroups=[Ref(SecurityGroup)]))

    #####################################################
    # AutoScaling Groups
    #####################################################
    AutoscalingGroup = t.add_resource(
        AutoScalingGroup(
            "AutoscalingGroup",
            DesiredCapacity=Ref(ScaleCapacity),
            Tags=[
                Tag("App", "cc-worker", True),
                Tag("Name", "cc-worker", True)
            ],
            LaunchConfigurationName=Ref(LaunchConfig),
            MinSize=Ref(ScaleCapacity),
            MaxSize=Ref(ScaleCapacity),
            VPCZoneIdentifier=[Ref(ApiSubnet1),
                               Ref(ApiSubnet2)],
            AvailabilityZones=[
                Ref(VPCAvailabilityZone1),
                Ref(VPCAvailabilityZone2)
            ],
            HealthCheckType="EC2",
            UpdatePolicy=UpdatePolicy(
                AutoScalingReplacingUpdate=AutoScalingReplacingUpdate(
                    WillReplace=True, ),
                AutoScalingRollingUpdate=AutoScalingRollingUpdate(
                    PauseTime='PT5M',
                    MinInstancesInService="1",
                    MaxBatchSize='1',
                    WaitOnResourceSignals=True))))

    return t.to_json()