Esempio n. 1
0
def extract_security_group_ids(configuration, elastigroup_config: dict, args):
    """
    This function identifies whether a senza formatted EC2-sg (by name) is configured,
    if so it transforms it into a Spotinst Elastigroup EC2-sq (by id) API configuration
    If there's already a compute.launchSpecification.securityGroupIds config it's left unchanged
    """
    elastigroup_config = ensure_keys(elastigroup_config, "compute",
                                     "launchSpecification")
    launch_spec_config = elastigroup_config["compute"]["launchSpecification"]

    security_group_ids = []
    if "securityGroupIds" not in launch_spec_config.keys():
        if "SecurityGroups" in configuration.keys():
            security_groups_ref = configuration.pop("SecurityGroups")

            if isinstance(security_groups_ref, str):
                security_group_ids = resolve_security_groups(
                    [security_groups_ref], args.region)

            elif isinstance(security_groups_ref, list):
                security_group_ids = resolve_security_groups(
                    security_groups_ref, args.region)

            if len(security_group_ids) > 0:
                launch_spec_config["securityGroupIds"] = security_group_ids
Esempio n. 2
0
def component_redis_cluster(definition, configuration, args, info, force):
    name = configuration["Name"]
    definition = ensure_keys(definition, "Resources")

    number_of_nodes = int(configuration.get('NumberOfNodes', '2'))

    definition["Resources"]["RedisReplicationGroup"] = {
        "Type": "AWS::ElastiCache::ReplicationGroup",
        "Properties": {
            "AutomaticFailoverEnabled": True,
            "CacheNodeType": configuration.get('CacheNodeType', 'cache.t2.small'),
            "CacheSubnetGroupName": {
                "Ref": "RedisSubnetGroup"
            },
            "Engine": "redis",
            "EngineVersion": configuration.get('EngineVersion', '2.8.19'),
            "CacheParameterGroupName": configuration.get('CacheParameterGroupName', 'default.redis2.8'),
            "NumCacheClusters": number_of_nodes,
            "CacheNodeType": configuration.get('CacheNodeType', 'cache.t2.small'),
            "SecurityGroupIds": resolve_security_groups(configuration["SecurityGroups"], args.region),
            "ReplicationGroupDescription": "Redis replicated cache cluster: " + name,
        }
    }

    definition["Resources"]["RedisSubnetGroup"] = {
        "Type": "AWS::ElastiCache::SubnetGroup",
        "Properties": {
            "Description": "Redis cluster subnet group",
            "SubnetIds": {"Fn::FindInMap": ["ServerSubnets", {"Ref": "AWS::Region"}, "Subnets"]}
        }
    }

    return definition
Esempio n. 3
0
def component_redis_node(definition, configuration, args, info, force, account_info):
    name = configuration["Name"]
    definition = ensure_keys(definition, "Resources")

    definition["Resources"]["RedisCacheCluster"] = {
        "Type": "AWS::ElastiCache::CacheCluster",
        "Properties": {
            "ClusterName": name,
            "Engine": "redis",
            "EngineVersion": configuration.get('EngineVersion', '2.8.19'),
            "CacheParameterGroupName": configuration.get('CacheParameterGroupName', 'default.redis2.8'),
            "NumCacheNodes": 1,
            "CacheNodeType": configuration.get('CacheNodeType', 'cache.t2.small'),
            "CacheSubnetGroupName": {
                "Ref": "RedisSubnetGroup"
            },
            "VpcSecurityGroupIds": resolve_security_groups(configuration["SecurityGroups"], args.region)
        }
    }

    definition["Resources"]["RedisSubnetGroup"] = {
        "Type": "AWS::ElastiCache::SubnetGroup",
        "Properties": {
            "Description": "Redis cluster subnet group",
            "SubnetIds": {"Fn::FindInMap": ["ServerSubnets", {"Ref": "AWS::Region"}, "Subnets"]}
        }
    }

    return definition
Esempio n. 4
0
def component_redis_cluster(definition, configuration, args, info, force, account_info):
    name = configuration["Name"]
    definition = ensure_keys(definition, "Resources")

    number_of_nodes = int(configuration.get('NumberOfNodes', '2'))

    definition["Resources"]["RedisReplicationGroup"] = {
        "Type": "AWS::ElastiCache::ReplicationGroup",
        "Properties": {
            "AutomaticFailoverEnabled": True,
            "CacheNodeType": configuration.get('CacheNodeType', 'cache.t2.small'),
            "CacheSubnetGroupName": {
                "Ref": "RedisSubnetGroup"
            },
            "Engine": "redis",
            "EngineVersion": configuration.get('EngineVersion', '2.8.19'),
            "CacheParameterGroupName": configuration.get('CacheParameterGroupName', 'default.redis2.8'),
            "NumCacheClusters": number_of_nodes,
            "CacheNodeType": configuration.get('CacheNodeType', 'cache.t2.small'),
            "SecurityGroupIds": resolve_security_groups(configuration["SecurityGroups"], args.region),
            "ReplicationGroupDescription": "Redis replicated cache cluster: " + name,
        }
    }

    definition["Resources"]["RedisSubnetGroup"] = {
        "Type": "AWS::ElastiCache::SubnetGroup",
        "Properties": {
            "Description": "Redis cluster subnet group",
            "SubnetIds": {"Fn::FindInMap": ["ServerSubnets", {"Ref": "AWS::Region"}, "Subnets"]}
        }
    }

    return definition
Esempio n. 5
0
def extract_security_group_ids(configuration, elastigroup_config: dict, args):
    """
    This function identifies whether a senza formatted EC2-sg (by name) is configured,
    if so it transforms it into a Spotinst Elastigroup EC2-sq (by id) API configuration
    If there's already a compute.launchSpecification.securityGroupIds config it's left unchanged
    """
    elastigroup_config = ensure_keys(elastigroup_config, "compute", "launchSpecification")
    launch_spec_config = elastigroup_config["compute"]["launchSpecification"]

    security_group_ids = []
    if "securityGroupIds" not in launch_spec_config.keys():
        if "SecurityGroups" in configuration.keys():
            security_groups_ref = configuration.pop("SecurityGroups")

            if isinstance(security_groups_ref, str):
                security_group_ids = resolve_security_groups([security_groups_ref], args.region)

            elif isinstance(security_groups_ref, list):
                security_group_ids = resolve_security_groups(security_groups_ref, args.region)

            if len(security_group_ids) > 0:
                launch_spec_config["securityGroupIds"] = security_group_ids
Esempio n. 6
0
def test_resolve_security_groups(monkeypatch):
    ec2 = MagicMock()
    ec2.security_groups.filter.return_value = [MagicMock(name='app-test', id='sg-test')]
    monkeypatch.setattr('boto3.resource', MagicMock(return_value=ec2))

    security_groups = []
    security_groups.append({'Fn::GetAtt': ['RefSecGroup', 'GroupId']})
    security_groups.append('sg-007')
    security_groups.append('app-test')

    result = []
    result.append({'Fn::GetAtt': ['RefSecGroup', 'GroupId']})
    result.append('sg-007')
    result.append('sg-test')

    assert result == resolve_security_groups(security_groups, 'myregion')
Esempio n. 7
0
def test_resolve_security_groups(monkeypatch):
    ec2 = MagicMock()
    ec2.security_groups.filter.return_value = [MagicMock(name='app-test', id='sg-test')]
    monkeypatch.setattr('boto3.resource', MagicMock(return_value=ec2))

    security_groups = []
    security_groups.append({'Fn::GetAtt': ['RefSecGroup', 'GroupId']})
    security_groups.append('sg-007')
    security_groups.append('app-test')

    result = []
    result.append({'Fn::GetAtt': ['RefSecGroup', 'GroupId']})
    result.append('sg-007')
    result.append('sg-test')

    assert result == resolve_security_groups(security_groups, 'myregion')
Esempio n. 8
0
def test_resolve_security_groups(monkeypatch):
    ec2 = MagicMock()
    sg = boto.ec2.securitygroup.SecurityGroup(name='app-test', id='sg-test')
    ec2.get_all_security_groups.return_value = [sg]
    monkeypatch.setattr('boto.ec2.connect_to_region', MagicMock(return_value=ec2))

    security_groups = []
    security_groups.append({'Fn::GetAtt': ['RefSecGroup', 'GroupId']})
    security_groups.append('sg-007')
    security_groups.append('app-test')

    result = []
    result.append({'Fn::GetAtt': ['RefSecGroup', 'GroupId']})
    result.append('sg-007')
    result.append('sg-test')

    assert result == resolve_security_groups(security_groups, 'myregion')
Esempio n. 9
0
def test_resolve_security_groups(monkeypatch):
    ec2 = MagicMock()
    ec2.security_groups.filter = MagicMock(
        side_effect=[
            [MagicMock(name="app-test", id="sg-test")],
            [MagicMock(name="physical-resource-id", id="sg-resource")],
        ]
    )

    def my_resource(rtype, *args):
        if rtype == "ec2":
            return ec2
        else:
            return MagicMock()

    def my_client(rtype, *args):
        if rtype == "cloudformation":
            cf = MagicMock()
            resource = {
                "StackResourceDetail": {
                    "ResourceStatus": "CREATE_COMPLETE",
                    "ResourceType": "AWS::EC2::SecurityGroup",
                    "PhysicalResourceId": "physical-resource-id",
                }
            }
            cf.describe_stack_resource.return_value = resource
            return cf
        else:
            return MagicMock()

    monkeypatch.setattr("boto3.resource", my_resource)
    monkeypatch.setattr("boto3.client", my_client)

    security_groups = []
    security_groups.append({"Fn::GetAtt": ["RefSecGroup", "GroupId"]})
    security_groups.append("sg-007")
    security_groups.append("app-test")
    security_groups.append({"Stack": "stack", "LogicalId": "id"})

    result = []
    result.append({"Fn::GetAtt": ["RefSecGroup", "GroupId"]})
    result.append("sg-007")
    result.append("sg-test")
    result.append("sg-resource")

    assert result == resolve_security_groups(security_groups, "myregion")
Esempio n. 10
0
def test_resolve_security_groups(monkeypatch):
    ec2 = MagicMock()
    ec2.security_groups.filter = MagicMock(side_effect=[[
        MagicMock(name='app-test', id='sg-test')
    ], [MagicMock(name='physical-resource-id', id='sg-resource')]])

    def my_resource(rtype, *args):
        if rtype == 'ec2':
            return ec2
        else:
            return MagicMock()

    def my_client(rtype, *args):
        if rtype == 'cloudformation':
            cf = MagicMock()
            resource = {
                'StackResourceDetail': {
                    'ResourceStatus': 'CREATE_COMPLETE',
                    'ResourceType': 'AWS::EC2::SecurityGroup',
                    'PhysicalResourceId': 'physical-resource-id'
                }
            }
            cf.describe_stack_resource.return_value = resource
            return cf
        else:
            return MagicMock()

    monkeypatch.setattr('boto3.resource', my_resource)
    monkeypatch.setattr('boto3.client', my_client)

    security_groups = []
    security_groups.append({'Fn::GetAtt': ['RefSecGroup', 'GroupId']})
    security_groups.append('sg-007')
    security_groups.append('app-test')
    security_groups.append({'Stack': 'stack', 'LogicalId': 'id'})

    result = []
    result.append({'Fn::GetAtt': ['RefSecGroup', 'GroupId']})
    result.append('sg-007')
    result.append('sg-test')
    result.append('sg-resource')

    assert result == resolve_security_groups(security_groups, 'myregion')
Esempio n. 11
0
def test_resolve_security_groups(monkeypatch):
    ec2 = MagicMock()
    ec2.security_groups.filter = MagicMock(side_effect=[
        [MagicMock(name='app-test', id='sg-test')],
        [MagicMock(name='physical-resource-id', id='sg-resource')]])

    def my_resource(rtype, *args):
        if rtype == 'ec2':
            return ec2
        else:
            return MagicMock()

    def my_client(rtype, *args):
        if rtype == 'cloudformation':
            cf = MagicMock()
            resource = {
                'StackResourceDetail': {'ResourceStatus': 'CREATE_COMPLETE',
                                        'ResourceType': 'AWS::EC2::SecurityGroup',
                                        'PhysicalResourceId': 'physical-resource-id'}}
            cf.describe_stack_resource.return_value = resource
            return cf
        else:
            return MagicMock()

    monkeypatch.setattr('boto3.resource', my_resource)
    monkeypatch.setattr('boto3.client', my_client)

    security_groups = []
    security_groups.append({'Fn::GetAtt': ['RefSecGroup', 'GroupId']})
    security_groups.append('sg-007')
    security_groups.append('app-test')
    security_groups.append({'Stack': 'stack', 'LogicalId': 'id'})

    result = []
    result.append({'Fn::GetAtt': ['RefSecGroup', 'GroupId']})
    result.append('sg-007')
    result.append('sg-test')
    result.append('sg-resource')

    assert result == resolve_security_groups(security_groups, 'myregion')
Esempio n. 12
0
def component_elastic_load_balancer_v2(definition,
                                       configuration: dict,
                                       args: TemplateArguments,
                                       info: dict,
                                       force,
                                       account_info: AccountArguments):
    lb_name = configuration["Name"]
    # domains pointing to the load balancer
    subdomain = ''
    main_zone = None
    for name, domain in configuration.get('Domains', {}).items():
        name = '{}{}'.format(lb_name, name)

        domain_name = "{0}.{1}".format(domain["Subdomain"], domain["Zone"])

        convert_cname_records_to_alias(domain_name)

        properties = {"Type": "A",
                      "Name": domain_name,
                      "HostedZoneName": domain["Zone"],
                      "AliasTarget": {"HostedZoneId": {"Fn::GetAtt": [lb_name,
                                                                      "CanonicalHostedZoneID"]},
                                      "DNSName": {"Fn::GetAtt": [lb_name, "DNSName"]}}}
        definition["Resources"][name] = {"Type": "AWS::Route53::RecordSet",
                                         "Properties": properties}

        if domain["Type"] == "weighted":
            definition["Resources"][name]["Properties"]['Weight'] = 0
            definition["Resources"][name]["Properties"]['SetIdentifier'] = "{0}-{1}".format(info["StackName"],
                                                                                            info["StackVersion"])
            subdomain = domain['Subdomain']
            main_zone = domain['Zone']  # type: str

    target_group_name = lb_name + 'TargetGroup'
    listeners = configuration.get('Listeners') or get_listeners(
        lb_name, target_group_name, subdomain, main_zone, configuration, account_info)

    health_check_protocol = configuration.get('HealthCheckProtocol') or 'HTTP'

    if health_check_protocol not in ALLOWED_HEALTH_CHECK_PROTOCOLS:
        raise click.UsageError('Protocol "{}" is not supported for LoadBalancer'.format(health_check_protocol))

    health_check_path = configuration.get("HealthCheckPath") or '/health'
    health_check_port = configuration.get("HealthCheckPort") or configuration["HTTPPort"]

    if configuration.get('LoadBalancerName'):
        loadbalancer_name = generate_valid_cloud_name(configuration["LoadBalancerName"], 32)
    elif configuration.get('NameSuffix'):
        version = '{}-{}'.format(info["StackVersion"],
                                 configuration['NameSuffix'])
        loadbalancer_name = get_load_balancer_name(info["StackName"], version)
        del(configuration['NameSuffix'])
    else:
        loadbalancer_name = get_load_balancer_name(info["StackName"],
                                                   info["StackVersion"])

    loadbalancer_scheme = configuration.get('Scheme') or 'internal'
    if loadbalancer_scheme == 'internet-facing':
        click.secho('You are deploying an internet-facing ELB that will be '
                    'publicly accessible! You should have OAUTH2 and HTTPS '
                    'in place!', bold=True, err=True)

    if loadbalancer_scheme not in ALLOWED_LOADBALANCER_SCHEMES:
        raise click.UsageError('Scheme "{}" is not supported for LoadBalancer'.format(loadbalancer_scheme))

    if loadbalancer_scheme == "internal":
        loadbalancer_subnet_map = "LoadBalancerInternalSubnets"
    else:
        loadbalancer_subnet_map = "LoadBalancerSubnets"

    vpc_id = configuration.get("VpcId") or account_info.VpcID

    tags = [
        # Tag "Name"
        {
            "Key": "Name",
            "Value": "{0}-{1}".format(info["StackName"], info["StackVersion"])
        },
        # Tag "StackName"
        {
            "Key": "StackName",
            "Value": info["StackName"],
        },
        # Tag "StackVersion"
        {
            "Key": "StackVersion",
            "Value": info["StackVersion"]
        }
    ]

    # load balancer
    definition["Resources"][lb_name] = {
        "Type": "AWS::ElasticLoadBalancingV2::LoadBalancer",
        "Properties": {
            'Name': loadbalancer_name,
            'Scheme': loadbalancer_scheme,
            'SecurityGroups': resolve_security_groups(configuration["SecurityGroups"], args.region),
            'Subnets': {"Fn::FindInMap": [loadbalancer_subnet_map, {"Ref": "AWS::Region"}, "Subnets"]},
            'LoadBalancerAttributes': [
                {
                    "Key": "idle_timeout.timeout_seconds",
                    "Value": "60"
                }
            ],
            "Tags": tags
        }
    }
    definition["Resources"][target_group_name] = {
        'Type': 'AWS::ElasticLoadBalancingV2::TargetGroup',
        'Properties': {
            'Name': loadbalancer_name,
            'HealthCheckIntervalSeconds': '10',
            'HealthCheckPath': health_check_path,
            'HealthCheckPort': health_check_port,
            'HealthCheckProtocol': health_check_protocol,
            'HealthCheckTimeoutSeconds': '5',
            'HealthyThresholdCount': '2',
            'Port': configuration['HTTPPort'],
            'Protocol': 'HTTP',
            'UnhealthyThresholdCount': '2',
            'VpcId': vpc_id,
            'Tags': tags,
            'TargetGroupAttributes': [{'Key': 'deregistration_delay.timeout_seconds', 'Value': '60'}]
        }
    }
    resource_names = set([lb_name, target_group_name])
    for i, listener in enumerate(listeners):
        if i == 0:
            suffix = ''
        else:
            suffix = str(i + 1)
        resource_name = lb_name + 'Listener' + suffix
        definition['Resources'][resource_name] = listener
        resource_names.add(resource_name)
    for key, val in configuration.items():
        # overwrite any specified properties, but only properties which were defined by us already
        for res in resource_names:
            if key in definition['Resources'][res]['Properties'] and key not in SENZA_PROPERTIES:
                definition['Resources'][res]['Properties'][key] = val
    return definition
Esempio n. 13
0
def component_elastic_load_balancer(definition, configuration, args, info,
                                    force, account_info):
    lb_name = configuration["Name"]

    # domains pointing to the load balancer
    main_zone = None
    for name, domain in configuration.get('Domains', {}).items():
        name = '{}{}'.format(lb_name, name)
        definition["Resources"][name] = {
            "Type": "AWS::Route53::RecordSet",
            "Properties": {
                "Type": "CNAME",
                "TTL": 20,
                "ResourceRecords": [{
                    "Fn::GetAtt": [lb_name, "DNSName"]
                }],
                "Name": "{0}.{1}".format(domain["Subdomain"], domain["Zone"]),
                "HostedZoneName": "{0}".format(domain["Zone"])
            },
        }

        if domain["Type"] == "weighted":
            definition["Resources"][name]["Properties"]['Weight'] = 0
            definition["Resources"][name]["Properties"][
                'SetIdentifier'] = "{0}-{1}".format(info["StackName"],
                                                    info["StackVersion"])
            main_zone = domain['Zone']

    ssl_cert = configuration.get('SSLCertificateId')

    pattern = None
    if not ssl_cert:
        if main_zone:
            pattern = main_zone.lower().rstrip('.').replace('.', '-')
        else:
            pattern = ''
    elif not ssl_cert.startswith('arn:'):
        pattern = ssl_cert

    if pattern is not None:
        ssl_cert = find_ssl_certificate_arn(args.region, pattern)

        if not ssl_cert:
            fatal_error(
                'Could not find any matching SSL certificate for "{}"'.format(
                    pattern))

    health_check_protocol = "HTTP"
    allowed_health_check_protocols = ("HTTP", "TCP", "UDP", "SSL")
    if "HealthCheckProtocol" in configuration:
        health_check_protocol = configuration["HealthCheckProtocol"]

    if health_check_protocol not in allowed_health_check_protocols:
        raise click.UsageError(
            'Protocol "{}" is not supported for LoadBalancer'.format(
                health_check_protocol))

    health_check_path = "/ui/"
    if "HealthCheckPath" in configuration:
        health_check_path = configuration["HealthCheckPath"]

    health_check_port = configuration["HTTPPort"]
    if "HealthCheckPort" in configuration:
        health_check_port = configuration["HealthCheckPort"]

    health_check_target = "{0}:{1}{2}".format(health_check_protocol,
                                              health_check_port,
                                              health_check_path)
    if configuration.get('NameSufix'):
        loadbalancer_name = get_load_balancer_name(
            info["StackName"], '{}-{}'.format(info["StackVersion"],
                                              configuration['NameSufix']))
        del (configuration['NameSufix'])
    else:
        loadbalancer_name = get_load_balancer_name(info["StackName"],
                                                   info["StackVersion"])

    loadbalancer_scheme = "internal"
    allowed_loadbalancer_schemes = ("internet-facing", "internal")
    if "Scheme" in configuration:
        loadbalancer_scheme = configuration["Scheme"]
    else:
        configuration["Scheme"] = loadbalancer_scheme

    if loadbalancer_scheme == 'internet-facing':
        click.secho(
            'You are deploying an internet-facing ELB that will be publicly accessible! '
            + 'You should have OAUTH2 and HTTPS in place!',
            fg='red',
            bold=True,
            err=True)

    if loadbalancer_scheme not in allowed_loadbalancer_schemes:
        raise click.UsageError(
            'Scheme "{}" is not supported for LoadBalancer'.format(
                loadbalancer_scheme))

    if loadbalancer_scheme == "internal":
        loadbalancer_subnet_map = "LoadBalancerInternalSubnets"
    else:
        loadbalancer_subnet_map = "LoadBalancerSubnets"

    # load balancer
    definition["Resources"][lb_name] = {
        "Type": "AWS::ElasticLoadBalancing::LoadBalancer",
        "Properties": {
            "Subnets": {
                "Fn::FindInMap":
                [loadbalancer_subnet_map, {
                    "Ref": "AWS::Region"
                }, "Subnets"]
            },
            "HealthCheck": {
                "HealthyThreshold": "2",
                "UnhealthyThreshold": "2",
                "Interval": "10",
                "Timeout": "5",
                "Target": health_check_target
            },
            "Listeners": [{
                "PolicyNames": [],
                "SSLCertificateId": ssl_cert,
                "Protocol": "HTTPS",
                "InstancePort": configuration["HTTPPort"],
                "LoadBalancerPort": 443
            }],
            "CrossZone":
            "true",
            "LoadBalancerName":
            loadbalancer_name,
            "SecurityGroups":
            resolve_security_groups(configuration["SecurityGroups"],
                                    args.region),
            "Tags": [
                # Tag "Name"
                {
                    "Key":
                    "Name",
                    "Value":
                    "{0}-{1}".format(info["StackName"], info["StackVersion"])
                },
                # Tag "StackName"
                {
                    "Key": "StackName",
                    "Value": info["StackName"],
                },
                # Tag "StackVersion"
                {
                    "Key": "StackVersion",
                    "Value": info["StackVersion"]
                }
            ]
        }
    }
    for key, val in configuration.items():
        # overwrite any specified properties, but
        # ignore our special Senza properties as they are not supported by CF
        if key not in SENZA_PROPERTIES:
            definition['Resources'][lb_name]['Properties'][key] = val

    return definition
Esempio n. 14
0
def component_elastic_load_balancer(definition, configuration: dict,
                                    args: TemplateArguments, info: dict, force,
                                    account_info: AccountArguments):
    lb_name = configuration["Name"]
    # domains pointing to the load balancer
    subdomain = ''
    main_zone = None
    for name, domain in configuration.get('Domains', {}).items():
        name = '{}{}'.format(lb_name, name)

        domain_name = "{0}.{1}".format(domain["Subdomain"], domain["Zone"])

        convert_cname_records_to_alias(domain_name)

        properties = {
            "Type": "A",
            "Name": domain_name,
            "HostedZoneName": domain["Zone"],
            "AliasTarget": {
                "HostedZoneId": {
                    "Fn::GetAtt": [lb_name, "CanonicalHostedZoneNameID"]
                },
                "DNSName": {
                    "Fn::GetAtt": [lb_name, "DNSName"]
                }
            }
        }
        definition["Resources"][name] = {
            "Type": "AWS::Route53::RecordSet",
            "Properties": properties
        }

        if domain["Type"] == "weighted":
            definition["Resources"][name]["Properties"]['Weight'] = 0
            definition["Resources"][name]["Properties"][
                'SetIdentifier'] = "{0}-{1}".format(info["StackName"],
                                                    info["StackVersion"])
            subdomain = domain['Subdomain']
            main_zone = domain['Zone']  # type: str

    listeners = configuration.get('Listeners') or get_listeners(configuration)
    listeners = resolve_ssl_certificates(listeners, subdomain, main_zone,
                                         account_info)

    health_check_protocol = configuration.get('HealthCheckProtocol') or 'HTTP'

    if health_check_protocol not in ALLOWED_HEALTH_CHECK_PROTOCOLS:
        raise click.UsageError(
            'Protocol "{}" is not supported for LoadBalancer'.format(
                health_check_protocol))

    if health_check_protocol in ['HTTP', 'HTTPS']:
        health_check_path = configuration.get("HealthCheckPath") or '/health'
    else:
        health_check_path = ''

    health_check_port = configuration.get(
        "HealthCheckPort") or configuration["HTTPPort"]

    health_check_target = "{0}:{1}{2}".format(health_check_protocol,
                                              health_check_port,
                                              health_check_path)

    if configuration.get('NameSuffix'):
        version = '{}-{}'.format(info["StackVersion"],
                                 configuration['NameSuffix'])
        loadbalancer_name = get_load_balancer_name(info["StackName"], version)
        del (configuration['NameSuffix'])
    else:
        loadbalancer_name = get_load_balancer_name(info["StackName"],
                                                   info["StackVersion"])

    loadbalancer_scheme = configuration.get('Scheme') or 'internal'

    if loadbalancer_scheme == 'internet-facing':
        click.secho(
            'You are deploying an internet-facing ELB that will be '
            'publicly accessible! You should have OAUTH2 and HTTPS '
            'in place!',
            bold=True,
            err=True)

    if loadbalancer_scheme not in ALLOWED_LOADBALANCER_SCHEMES:
        raise click.UsageError(
            'Scheme "{}" is not supported for LoadBalancer'.format(
                loadbalancer_scheme))

    if loadbalancer_scheme == "internal":
        loadbalancer_subnet_map = "LoadBalancerInternalSubnets"
    else:
        loadbalancer_subnet_map = "LoadBalancerSubnets"

    # load balancer
    definition["Resources"][lb_name] = {
        "Type": "AWS::ElasticLoadBalancing::LoadBalancer",
        "Properties": {
            "Scheme":
            loadbalancer_scheme,
            "Subnets": {
                "Fn::FindInMap":
                [loadbalancer_subnet_map, {
                    "Ref": "AWS::Region"
                }, "Subnets"]
            },
            "HealthCheck": {
                "HealthyThreshold": "2",
                "UnhealthyThreshold": "2",
                "Interval": "10",
                "Timeout": "5",
                "Target": health_check_target
            },
            "Listeners":
            listeners,
            "ConnectionDrainingPolicy": {
                "Enabled": True,
                "Timeout": 60
            },
            "CrossZone":
            "true",
            "LoadBalancerName":
            loadbalancer_name,
            "SecurityGroups":
            resolve_security_groups(configuration["SecurityGroups"],
                                    args.region),
            "Tags": [
                # Tag "Name"
                {
                    "Key":
                    "Name",
                    "Value":
                    "{0}-{1}".format(info["StackName"], info["StackVersion"])
                },
                # Tag "StackName"
                {
                    "Key": "StackName",
                    "Value": info["StackName"],
                },
                # Tag "StackVersion"
                {
                    "Key": "StackVersion",
                    "Value": info["StackVersion"]
                }
            ]
        }
    }
    for key, val in configuration.items():
        # overwrite any specified properties, but
        # ignore our special Senza properties as they are not supported by CF
        if key not in SENZA_PROPERTIES:
            definition['Resources'][lb_name]['Properties'][key] = val

    return definition
Esempio n. 15
0
def component_elastic_load_balancer(definition, configuration, args, info, force):
    lb_name = configuration["Name"]

    # domains pointing to the load balancer
    main_zone = None
    for name, domain in configuration.get('Domains', {}).items():
        definition["Resources"][name] = {
            "Type": "AWS::Route53::RecordSet",
            "Properties": {
                "Type": "CNAME",
                "TTL": 20,
                "ResourceRecords": [
                    {"Fn::GetAtt": [lb_name, "DNSName"]}
                ],
                "Name": "{0}.{1}".format(domain["Subdomain"], domain["Zone"]),
                "HostedZoneName": "{0}.".format(domain["Zone"])
            },
        }

        if domain["Type"] == "weighted":
            definition["Resources"][name]["Properties"]['Weight'] = 0
            definition["Resources"][name]["Properties"]['SetIdentifier'] = "{0}-{1}".format(info["StackName"],
                                                                                            info["StackVersion"])
            main_zone = domain['Zone']

    ssl_cert = configuration.get('SSLCertificateId')

    pattern = None
    if not ssl_cert:
        if main_zone:
            pattern = main_zone.lower().replace('.', '-')
        else:
            pattern = ''
    elif not ssl_cert.startswith('arn:'):
        pattern = ssl_cert

    if pattern is not None:
        ssl_cert = find_ssl_certificate_arn(args.region, pattern)

        if not ssl_cert:
            raise click.UsageError('Could not find any matching SSL certificate for "{}"'.format(pattern))

    health_check_protocol = "HTTP"
    allowed_health_check_protocols = ("HTTP", "TCP", "UDP", "SSL")
    if "HealthCheckProtocol" in configuration:
        health_check_protocol = configuration["HealthCheckProtocol"]

    if health_check_protocol not in allowed_health_check_protocols:
        raise click.UsageError('Protocol "{}" is not supported for LoadBalancer'.format(health_check_protocol))

    health_check_path = "/ui/"
    if "HealthCheckPath" in configuration:
        health_check_path = configuration["HealthCheckPath"]

    health_check_port = configuration["HTTPPort"]
    if "HealthCheckPort" in configuration:
        health_check_port = configuration["HealthCheckPort"]

    health_check_target = "{0}:{1}{2}".format(health_check_protocol, health_check_port, health_check_path)
    loadbalancer_name = get_load_balancer_name(info["StackName"], info["StackVersion"])

    loadbalancer_scheme = "internet-facing"
    allowed_loadbalancer_schemes = ("internet-facing", "internal")
    if "Scheme" in configuration:
        loadbalancer_scheme = configuration["Scheme"]

    if loadbalancer_scheme not in allowed_loadbalancer_schemes:
        raise click.UsageError('Scheme "{}" is not supported for LoadBalancer'.format(loadbalancer_scheme))

    if loadbalancer_scheme == "internal":
        loadbalancer_subnet_map = "LoadBalancerInternalSubnets"
    else:
        loadbalancer_subnet_map = "LoadBalancerSubnets"

    # load balancer
    definition["Resources"][lb_name] = {
        "Type": "AWS::ElasticLoadBalancing::LoadBalancer",
        "Properties": {
            "Scheme": loadbalancer_scheme,
            "Subnets": {"Fn::FindInMap": [loadbalancer_subnet_map, {"Ref": "AWS::Region"}, "Subnets"]},
            "HealthCheck": {
                "HealthyThreshold": "2",
                "UnhealthyThreshold": "2",
                "Interval": "10",
                "Timeout": "5",
                "Target": health_check_target
            },
            "Listeners": [
                {
                    "PolicyNames": [],
                    "SSLCertificateId": ssl_cert,
                    "Protocol": "HTTPS",
                    "InstancePort": configuration["HTTPPort"],
                    "LoadBalancerPort": 443
                }
            ],
            "CrossZone": "true",
            "LoadBalancerName": loadbalancer_name,
            "SecurityGroups": resolve_security_groups(configuration["SecurityGroups"], args.region),
            "Tags": [
                # Tag "Name"
                {
                    "Key": "Name",
                    "Value": "{0}-{1}".format(info["StackName"], info["StackVersion"])
                },
                # Tag "StackName"
                {
                    "Key": "StackName",
                    "Value": info["StackName"],
                },
                # Tag "StackVersion"
                {
                    "Key": "StackVersion",
                    "Value": info["StackVersion"]
                }
            ]
        }
    }

    return definition
Esempio n. 16
0
def component_elastic_load_balancer_v2(definition, configuration: dict,
                                       args: TemplateArguments, info: dict,
                                       force, account_info: AccountArguments):
    lb_name = configuration["Name"]
    # domains pointing to the load balancer
    subdomain = ''
    main_zone = None
    for name, domain in configuration.get('Domains', {}).items():
        name = '{}{}'.format(lb_name, name)

        domain_name = "{0}.{1}".format(domain["Subdomain"], domain["Zone"])

        convert_cname_records_to_alias(domain_name)

        properties = {
            "Type": "A",
            "Name": domain_name,
            "HostedZoneName": domain["Zone"],
            "AliasTarget": {
                "HostedZoneId": {
                    "Fn::GetAtt": [lb_name, "CanonicalHostedZoneID"]
                },
                "DNSName": {
                    "Fn::GetAtt": [lb_name, "DNSName"]
                }
            }
        }
        definition["Resources"][name] = {
            "Type": "AWS::Route53::RecordSet",
            "Properties": properties
        }

        if domain["Type"] == "weighted":
            definition["Resources"][name]["Properties"]['Weight'] = 0
            definition["Resources"][name]["Properties"][
                'SetIdentifier'] = "{0}-{1}".format(info["StackName"],
                                                    info["StackVersion"])
            subdomain = domain['Subdomain']
            main_zone = domain['Zone']  # type: str

    target_group_name = lb_name + 'TargetGroup'
    listeners = configuration.get('Listeners') or get_listeners(
        lb_name, target_group_name, subdomain, main_zone, configuration,
        account_info)

    health_check_protocol = configuration.get('HealthCheckProtocol') or 'HTTP'

    if health_check_protocol not in ALLOWED_HEALTH_CHECK_PROTOCOLS:
        raise click.UsageError(
            'Protocol "{}" is not supported for LoadBalancer'.format(
                health_check_protocol))

    health_check_path = configuration.get("HealthCheckPath") or '/health'
    health_check_port = configuration.get(
        "HealthCheckPort") or configuration["HTTPPort"]

    if configuration.get('NameSuffix'):
        version = '{}-{}'.format(info["StackVersion"],
                                 configuration['NameSuffix'])
        loadbalancer_name = get_load_balancer_name(info["StackName"], version)
        del (configuration['NameSuffix'])
    else:
        loadbalancer_name = get_load_balancer_name(info["StackName"],
                                                   info["StackVersion"])

    loadbalancer_scheme = configuration.get('Scheme') or 'internal'
    if loadbalancer_scheme == 'internet-facing':
        click.secho(
            'You are deploying an internet-facing ELB that will be '
            'publicly accessible! You should have OAUTH2 and HTTPS '
            'in place!',
            bold=True,
            err=True)

    if loadbalancer_scheme not in ALLOWED_LOADBALANCER_SCHEMES:
        raise click.UsageError(
            'Scheme "{}" is not supported for LoadBalancer'.format(
                loadbalancer_scheme))

    if loadbalancer_scheme == "internal":
        loadbalancer_subnet_map = "LoadBalancerInternalSubnets"
    else:
        loadbalancer_subnet_map = "LoadBalancerSubnets"

    vpc_id = configuration.get("VpcId") or account_info.VpcID

    tags = [
        # Tag "Name"
        {
            "Key": "Name",
            "Value": "{0}-{1}".format(info["StackName"], info["StackVersion"])
        },
        # Tag "StackName"
        {
            "Key": "StackName",
            "Value": info["StackName"],
        },
        # Tag "StackVersion"
        {
            "Key": "StackVersion",
            "Value": info["StackVersion"]
        }
    ]

    # load balancer
    definition["Resources"][lb_name] = {
        "Type": "AWS::ElasticLoadBalancingV2::LoadBalancer",
        "Properties": {
            'Name':
            loadbalancer_name,
            'Scheme':
            loadbalancer_scheme,
            'SecurityGroups':
            resolve_security_groups(configuration["SecurityGroups"],
                                    args.region),
            'Subnets': {
                "Fn::FindInMap":
                [loadbalancer_subnet_map, {
                    "Ref": "AWS::Region"
                }, "Subnets"]
            },
            "Tags":
            tags
        }
    }
    definition["Resources"][target_group_name] = {
        'Type': 'AWS::ElasticLoadBalancingV2::TargetGroup',
        'Properties': {
            'Name':
            loadbalancer_name,
            'HealthCheckIntervalSeconds':
            '10',
            'HealthCheckPath':
            health_check_path,
            'HealthCheckPort':
            health_check_port,
            'HealthCheckProtocol':
            health_check_protocol,
            'HealthCheckTimeoutSeconds':
            '5',
            'HealthyThresholdCount':
            '2',
            'Port':
            configuration['HTTPPort'],
            'Protocol':
            'HTTP',
            'UnhealthyThresholdCount':
            '2',
            'VpcId':
            vpc_id,
            'Tags':
            tags,
            'TargetGroupAttributes': [{
                'Key': 'deregistration_delay.timeout_seconds',
                'Value': '60'
            }]
        }
    }
    resource_names = set([lb_name, target_group_name])
    for i, listener in enumerate(listeners):
        if i == 0:
            suffix = ''
        else:
            suffix = str(i + 1)
        resource_name = lb_name + 'Listener' + suffix
        definition['Resources'][resource_name] = listener
        resource_names.add(resource_name)
    for key, val in configuration.items():
        # overwrite any specified properties, but only properties which were defined by us already
        for res in resource_names:
            if key in definition['Resources'][res][
                    'Properties'] and key not in SENZA_PROPERTIES:
                definition['Resources'][res]['Properties'][key] = val
    return definition
Esempio n. 17
0
def component_elastic_load_balancer(definition,
                                    configuration: dict,
                                    args: TemplateArguments,
                                    info: dict,
                                    force,
                                    account_info: AccountArguments):
    lb_name = configuration["Name"]
    # domains pointing to the load balancer
    subdomain = ''
    main_zone = None
    for name, domain in configuration.get('Domains', {}).items():
        name = '{}{}'.format(lb_name, name)

        domain_name = "{0}.{1}".format(domain["Subdomain"], domain["Zone"])

        convert_cname_records_to_alias(domain_name)

        properties = {"Type": "A",
                      "Name": domain_name,
                      "HostedZoneName": domain["Zone"],
                      "AliasTarget": {"HostedZoneId": {"Fn::GetAtt": [lb_name,
                                                                      "CanonicalHostedZoneNameID"]},
                                      "DNSName": {"Fn::GetAtt": [lb_name, "DNSName"]}}}
        definition["Resources"][name] = {"Type": "AWS::Route53::RecordSet",
                                         "Properties": properties}

        if domain["Type"] == "weighted":
            definition["Resources"][name]["Properties"]['Weight'] = 0
            definition["Resources"][name]["Properties"]['SetIdentifier'] = "{0}-{1}".format(info["StackName"],
                                                                                            info["StackVersion"])
            subdomain = domain['Subdomain']
            main_zone = domain['Zone']  # type: str

    listeners = configuration.get('Listeners') or get_listeners(configuration)
    listeners = resolve_ssl_certificates(listeners, subdomain, main_zone, account_info)

    health_check_protocol = configuration.get('HealthCheckProtocol') or 'HTTP'

    if health_check_protocol not in ALLOWED_HEALTH_CHECK_PROTOCOLS:
        raise click.UsageError('Protocol "{}" is not supported for LoadBalancer'.format(health_check_protocol))

    if health_check_protocol in ['HTTP', 'HTTPS']:
        health_check_path = configuration.get("HealthCheckPath") or '/health'
    else:
        health_check_path = ''

    health_check_port = configuration.get("HealthCheckPort") or configuration["HTTPPort"]

    health_check_target = "{0}:{1}{2}".format(health_check_protocol,
                                              health_check_port,
                                              health_check_path)

    if configuration.get('NameSuffix'):
        version = '{}-{}'.format(info["StackVersion"],
                                 configuration['NameSuffix'])
        loadbalancer_name = get_load_balancer_name(info["StackName"], version)
        del(configuration['NameSuffix'])
    else:
        loadbalancer_name = get_load_balancer_name(info["StackName"],
                                                   info["StackVersion"])

    loadbalancer_scheme = configuration.get('Scheme') or 'internal'

    if loadbalancer_scheme == 'internet-facing':
        click.secho('You are deploying an internet-facing ELB that will be '
                    'publicly accessible! You should have OAUTH2 and HTTPS '
                    'in place!', bold=True, err=True)

    if loadbalancer_scheme not in ALLOWED_LOADBALANCER_SCHEMES:
        raise click.UsageError('Scheme "{}" is not supported for LoadBalancer'.format(loadbalancer_scheme))

    if loadbalancer_scheme == "internal":
        loadbalancer_subnet_map = "LoadBalancerInternalSubnets"
    else:
        loadbalancer_subnet_map = "LoadBalancerSubnets"

    # load balancer
    definition["Resources"][lb_name] = {
        "Type": "AWS::ElasticLoadBalancing::LoadBalancer",
        "Properties": {
            "Scheme": loadbalancer_scheme,
            "Subnets": {"Fn::FindInMap": [loadbalancer_subnet_map, {"Ref": "AWS::Region"}, "Subnets"]},
            "HealthCheck": {
                "HealthyThreshold": "2",
                "UnhealthyThreshold": "2",
                "Interval": "10",
                "Timeout": "5",
                "Target": health_check_target
            },
            "Listeners": listeners,
            "ConnectionDrainingPolicy": {
                "Enabled": True,
                "Timeout": 60
            },
            "CrossZone": "true",
            "LoadBalancerName": loadbalancer_name,
            "SecurityGroups": resolve_security_groups(configuration["SecurityGroups"], args.region),
            "Tags": [
                # Tag "Name"
                {
                    "Key": "Name",
                    "Value": "{0}-{1}".format(info["StackName"], info["StackVersion"])
                },
                # Tag "StackName"
                {
                    "Key": "StackName",
                    "Value": info["StackName"],
                },
                # Tag "StackVersion"
                {
                    "Key": "StackVersion",
                    "Value": info["StackVersion"]
                }
            ]
        }
    }
    for key, val in configuration.items():
        # overwrite any specified properties, but
        # ignore our special Senza properties as they are not supported by CF
        if key not in SENZA_PROPERTIES:
            definition['Resources'][lb_name]['Properties'][key] = val

    return definition
Esempio n. 18
0
def component_elastic_load_balancer(definition, configuration, args, info, force, account_info):
    lb_name = configuration["Name"]

    # domains pointing to the load balancer
    subdomain = ''
    main_zone = None
    for name, domain in configuration.get('Domains', {}).items():
        name = '{}{}'.format(lb_name, name)
        definition["Resources"][name] = {
            "Type": "AWS::Route53::RecordSet",
            "Properties": {
                "Type": "CNAME",
                "TTL": 20,
                "ResourceRecords": [
                    {"Fn::GetAtt": [lb_name, "DNSName"]}
                ],
                "Name": "{0}.{1}".format(domain["Subdomain"], domain["Zone"]),
                "HostedZoneName": "{0}".format(domain["Zone"])
            },
        }

        if domain["Type"] == "weighted":
            definition["Resources"][name]["Properties"]['Weight'] = 0
            definition["Resources"][name]["Properties"]['SetIdentifier'] = "{0}-{1}".format(info["StackName"],
                                                                                            info["StackVersion"])
            subdomain = domain['Subdomain']
            main_zone = domain['Zone']  # type: str

    ssl_cert = configuration.get('SSLCertificateId')

    if ACMCertificate.arn_is_acm_certificate(ssl_cert):
        # check if certificate really exists
        try:
            ACMCertificate.get_by_arn(ssl_cert)
        except ClientError as e:
            error_msg = e.response['Error']['Message']
            fatal_error(error_msg)
    elif IAMServerCertificate.arn_is_server_certificate(ssl_cert):
        # TODO check if certificate exists
        pass
    elif ssl_cert is not None:
        certificate = IAMServerCertificate.get_by_name(ssl_cert)
        ssl_cert = certificate.arn
    elif main_zone is not None:
        if main_zone:
            iam_pattern = main_zone.lower().rstrip('.').replace('.', '-')
            name = '{sub}.{zone}'.format(sub=subdomain,
                                         zone=main_zone.rstrip('.'))
            acm_certificates = sorted(ACM.get_certificates(domain_name=name),
                                      reverse=True)
        else:
            iam_pattern = ''
            acm_certificates = []

        iam_certificates = sorted(IAM.get_certificates(name=iam_pattern))
        if not iam_certificates:
            # if there are no iam certificates matching the pattern
            # try to use any certificate
            iam_certificates = sorted(IAM.get_certificates(), reverse=True)

        # the priority is acm_certificate first and iam_certificate second
        certificates = (acm_certificates +
                        iam_certificates)  # type: List[Union[ACMCertificate, IAMServerCertificate]]
        try:
            certificate = certificates[0]
            ssl_cert = certificate.arn
        except IndexError:
            if main_zone:
                fatal_error('Could not find any matching '
                            'SSL certificate for "{}"'.format(name))
            else:
                fatal_error('Could not find any SSL certificate')

    health_check_protocol = "HTTP"
    allowed_health_check_protocols = ("HTTP", "TCP", "UDP", "SSL")
    if "HealthCheckProtocol" in configuration:
        health_check_protocol = configuration["HealthCheckProtocol"]

    if health_check_protocol not in allowed_health_check_protocols:
        raise click.UsageError('Protocol "{}" is not supported for LoadBalancer'.format(health_check_protocol))

    health_check_path = "/ui/"
    if "HealthCheckPath" in configuration:
        health_check_path = configuration["HealthCheckPath"]

    health_check_port = configuration["HTTPPort"]
    if "HealthCheckPort" in configuration:
        health_check_port = configuration["HealthCheckPort"]

    health_check_target = "{0}:{1}{2}".format(health_check_protocol,
                                              health_check_port,
                                              health_check_path)

    if configuration.get('LoadBalancerName'):
        loadbalancer_name = info["LoadBalancerName"]
    elif configuration.get('NameSuffix'):
        version = '{}-{}'.format(info["StackVersion"],
                                 configuration['NameSuffix'])
        loadbalancer_name = get_load_balancer_name(info["StackName"], version)
        del(configuration['NameSuffix'])
    else:
        loadbalancer_name = get_load_balancer_name(info["StackName"],
                                                   info["StackVersion"])

    loadbalancer_scheme = "internal"
    allowed_loadbalancer_schemes = ("internet-facing", "internal")
    if "Scheme" in configuration:
        loadbalancer_scheme = configuration["Scheme"]
    else:
        configuration["Scheme"] = loadbalancer_scheme

    if loadbalancer_scheme == 'internet-facing':
        click.secho('You are deploying an internet-facing ELB that will be '
                    'publicly accessible! You should have OAUTH2 and HTTPS '
                    'in place!', bold=True, err=True)

    if loadbalancer_scheme not in allowed_loadbalancer_schemes:
        raise click.UsageError('Scheme "{}" is not supported for LoadBalancer'.format(loadbalancer_scheme))

    if loadbalancer_scheme == "internal":
        loadbalancer_subnet_map = "LoadBalancerInternalSubnets"
    else:
        loadbalancer_subnet_map = "LoadBalancerSubnets"

    # load balancer
    definition["Resources"][lb_name] = {
        "Type": "AWS::ElasticLoadBalancing::LoadBalancer",
        "Properties": {
            "Subnets": {"Fn::FindInMap": [loadbalancer_subnet_map, {"Ref": "AWS::Region"}, "Subnets"]},
            "HealthCheck": {
                "HealthyThreshold": "2",
                "UnhealthyThreshold": "2",
                "Interval": "10",
                "Timeout": "5",
                "Target": health_check_target
            },
            "Listeners": [
                {
                    "PolicyNames": [],
                    "SSLCertificateId": ssl_cert,
                    "Protocol": "HTTPS",
                    "InstancePort": configuration["HTTPPort"],
                    "LoadBalancerPort": 443
                }
            ],
            "ConnectionDrainingPolicy": {
                "Enabled": True,
                "Timeout": 60
            },
            "CrossZone": "true",
            "LoadBalancerName": loadbalancer_name,
            "SecurityGroups": resolve_security_groups(configuration["SecurityGroups"], args.region),
            "Tags": [
                # Tag "Name"
                {
                    "Key": "Name",
                    "Value": "{0}-{1}".format(info["StackName"], info["StackVersion"])
                },
                # Tag "StackName"
                {
                    "Key": "StackName",
                    "Value": info["StackName"],
                },
                # Tag "StackVersion"
                {
                    "Key": "StackVersion",
                    "Value": info["StackVersion"]
                }
            ]
        }
    }
    for key, val in configuration.items():
        # overwrite any specified properties, but
        # ignore our special Senza properties as they are not supported by CF
        if key not in SENZA_PROPERTIES:
            definition['Resources'][lb_name]['Properties'][key] = val

    return definition
Esempio n. 19
0
def component_auto_scaling_group(definition, configuration, args, info, force, account_info):
    definition = ensure_keys(definition, "Resources")

    # launch configuration
    config_name = configuration["Name"] + "Config"
    definition["Resources"][config_name] = {
        "Type": "AWS::AutoScaling::LaunchConfiguration",
        "Properties": {
            "InstanceType": configuration["InstanceType"],
            "ImageId": {"Fn::FindInMap": ["Images", {"Ref": "AWS::Region"}, configuration["Image"]]},
            "AssociatePublicIpAddress": configuration.get('AssociatePublicIpAddress', False),
            "EbsOptimized": configuration.get('EbsOptimized', False)
        }
    }

    if 'BlockDeviceMappings' in configuration:
        definition['Resources'][config_name]['Properties']['BlockDeviceMappings'] = configuration['BlockDeviceMappings']

    if "IamInstanceProfile" in configuration:
        definition["Resources"][config_name]["Properties"]["IamInstanceProfile"] = configuration["IamInstanceProfile"]

    if 'IamRoles' in configuration:
        logical_id = configuration['Name'] + 'InstanceProfile'
        roles = configuration['IamRoles']
        if len(roles) > 1:
            for role in roles:
                if isinstance(role, dict):
                    raise click.UsageError('Cannot merge policies of Cloud Formation references ({"Ref": ".."}): ' +
                                           'You can use at most one IAM role with "Ref".')
            logical_role_id = configuration['Name'] + 'Role'
            definition['Resources'][logical_role_id] = {
                'Type': 'AWS::IAM::Role',
                'Properties': {
                    "AssumeRolePolicyDocument": {
                        "Version": "2012-10-17",
                        "Statement": [
                            {
                                "Effect": "Allow",
                                "Principal": {
                                    "Service": ["ec2.amazonaws.com"]
                                },
                                "Action": ["sts:AssumeRole"]
                            }
                        ]
                    },
                    'Path': '/',
                    'Policies': get_merged_policies(roles)
                }
            }
            instance_profile_roles = [{'Ref': logical_role_id}]
        else:
            instance_profile_roles = roles
        definition['Resources'][logical_id] = {
            'Type': 'AWS::IAM::InstanceProfile',
            'Properties': {
                'Path': '/',
                'Roles': instance_profile_roles
            }
        }
        definition["Resources"][config_name]["Properties"]["IamInstanceProfile"] = {'Ref': logical_id}

    if "SecurityGroups" in configuration:
        definition["Resources"][config_name]["Properties"]["SecurityGroups"] = \
            resolve_security_groups(configuration["SecurityGroups"], args.region)

    if "UserData" in configuration:
        definition["Resources"][config_name]["Properties"]["UserData"] = {
            "Fn::Base64": configuration["UserData"]
        }

    # auto scaling group
    asg_name = configuration["Name"]
    definition["Resources"][asg_name] = {
        "Type": "AWS::AutoScaling::AutoScalingGroup",
        # wait up to 15 minutes to get a signal from at least one server that it booted
        "CreationPolicy": {
            "ResourceSignal": {
                "Count": "1",
                "Timeout": "PT15M"
            }
        },
        "Properties": {
            # for our operator some notifications
            "LaunchConfigurationName": {"Ref": config_name},
            "VPCZoneIdentifier": {"Fn::FindInMap": ["ServerSubnets", {"Ref": "AWS::Region"}, "Subnets"]},
            "Tags": [
                # Tag "Name"
                {
                    "Key": "Name",
                    "PropagateAtLaunch": True,
                    "Value": "{0}-{1}".format(info["StackName"], info["StackVersion"])
                },
                # Tag "StackName"
                {
                    "Key": "StackName",
                    "PropagateAtLaunch": True,
                    "Value": info["StackName"],
                },
                # Tag "StackVersion"
                {
                    "Key": "StackVersion",
                    "PropagateAtLaunch": True,
                    "Value": info["StackVersion"]
                }
            ]
        }
    }

    if "OperatorTopicId" in info:
        definition["Resources"][asg_name]["Properties"]["NotificationConfiguration"] = {
            "NotificationTypes": [
                "autoscaling:EC2_INSTANCE_LAUNCH",
                "autoscaling:EC2_INSTANCE_LAUNCH_ERROR",
                "autoscaling:EC2_INSTANCE_TERMINATE",
                "autoscaling:EC2_INSTANCE_TERMINATE_ERROR"
            ],
            "TopicARN": resolve_topic_arn(args.region, info["OperatorTopicId"])
        }

    default_health_check_type = 'EC2'

    if "ElasticLoadBalancer" in configuration:
        if isinstance(configuration["ElasticLoadBalancer"], str):
            definition["Resources"][asg_name]["Properties"]["LoadBalancerNames"] = [
                {"Ref": configuration["ElasticLoadBalancer"]}]
        elif isinstance(configuration["ElasticLoadBalancer"], list):
            definition["Resources"][asg_name]["Properties"]["LoadBalancerNames"] = []
            for ref in configuration["ElasticLoadBalancer"]:
                definition["Resources"][asg_name]["Properties"]["LoadBalancerNames"].append({'Ref': ref})
        # use ELB health check by default
        default_health_check_type = 'ELB'

    definition["Resources"][asg_name]['Properties']['HealthCheckType'] = \
        configuration.get('HealthCheckType', default_health_check_type)
    definition["Resources"][asg_name]['Properties']['HealthCheckGracePeriod'] = \
        configuration.get('HealthCheckGracePeriod', 300)

    if "AutoScaling" in configuration:
        definition["Resources"][asg_name]["Properties"]["MaxSize"] = configuration["AutoScaling"]["Maximum"]
        definition["Resources"][asg_name]["Properties"]["MinSize"] = configuration["AutoScaling"]["Minimum"]

        # ScaleUp policy
        definition["Resources"][asg_name + "ScaleUp"] = {
            "Type": "AWS::AutoScaling::ScalingPolicy",
            "Properties": {
                "AdjustmentType": "ChangeInCapacity",
                "ScalingAdjustment": "1",
                "Cooldown": "60",
                "AutoScalingGroupName": {
                    "Ref": asg_name
                }
            }
        }

        # ScaleDown policy
        definition["Resources"][asg_name + "ScaleDown"] = {
            "Type": "AWS::AutoScaling::ScalingPolicy",
            "Properties": {
                "AdjustmentType": "ChangeInCapacity",
                "ScalingAdjustment": "-1",
                "Cooldown": "60",
                "AutoScalingGroupName": {
                    "Ref": asg_name
                }
            }
        }

        metric_type = configuration["AutoScaling"]["MetricType"]
        metricfn = globals().get('metric_{}'.format(metric_type.lower()))
        if not metricfn:
            raise click.UsageError('Auto scaling MetricType "{}" not supported.'.format(metric_type))
        definition = metricfn(asg_name, definition, configuration["AutoScaling"], args, info, force)
    else:
        definition["Resources"][asg_name]["Properties"]["MaxSize"] = 1
        definition["Resources"][asg_name]["Properties"]["MinSize"] = 1

    return definition
Esempio n. 20
0
def component_auto_scaling_group(definition, configuration, args, info, force,
                                 account_info):
    definition = ensure_keys(definition, "Resources")

    # launch configuration
    config_name = configuration["Name"] + "Config"
    definition["Resources"][config_name] = {
        "Type": "AWS::AutoScaling::LaunchConfiguration",
        "Properties": {
            "InstanceType":
            configuration["InstanceType"],
            "ImageId": {
                "Fn::FindInMap": [
                    "Images",
                    {
                        "Ref": "AWS::Region"
                    },
                    configuration["Image"],
                ]
            },
            "AssociatePublicIpAddress":
            configuration.get("AssociatePublicIpAddress", False),
            "EbsOptimized":
            configuration.get("EbsOptimized", False),
        },
    }

    if "IamRoles" in configuration:
        logical_id = handle_iam_roles(definition, configuration, args)
        definition["Resources"][config_name]["Properties"][
            "IamInstanceProfile"] = {
                "Ref": logical_id
            }

    if "SecurityGroups" in configuration:
        definition["Resources"][config_name]["Properties"][
            "SecurityGroups"] = resolve_security_groups(
                configuration["SecurityGroups"], args.region)

    if "UserData" in configuration:
        definition["Resources"][config_name]["Properties"]["UserData"] = {
            "Fn::Base64": configuration["UserData"]
        }

    # auto scaling group
    asg_name = configuration["Name"]
    asg_success = ["1", "PT15M"]
    if "AutoScaling" in configuration:
        if "SuccessRequires" in configuration["AutoScaling"]:
            asg_success = normalize_asg_success(
                configuration["AutoScaling"]["SuccessRequires"])

    tags = [
        # Tag "Name"
        {
            "Key": "Name",
            "PropagateAtLaunch": True,
            "Value": "{0}-{1}".format(info["StackName"], info["StackVersion"]),
        },
        # Tag "StackName"
        {
            "Key": "StackName",
            "PropagateAtLaunch": True,
            "Value": info["StackName"]
        },
        # Tag "StackVersion"
        {
            "Key": "StackVersion",
            "PropagateAtLaunch": True,
            "Value": info["StackVersion"],
        },
    ]

    if "Tags" in configuration:
        for tag in configuration["Tags"]:
            tags.append({
                "Key": tag["Key"],
                "PropagateAtLaunch": True,
                "Value": tag["Value"]
            })

    definition["Resources"][asg_name] = {
        "Type": "AWS::AutoScaling::AutoScalingGroup",
        # wait to get a signal from an amount of servers to signal that it booted
        "CreationPolicy": {
            "ResourceSignal": {
                "Count": asg_success[0],
                "Timeout": asg_success[1]
            }
        },
        "Properties": {
            # for our operator some notifications
            "LaunchConfigurationName": {
                "Ref": config_name
            },
            "VPCZoneIdentifier": {
                "Fn::FindInMap":
                ["ServerSubnets", {
                    "Ref": "AWS::Region"
                }, "Subnets"]
            },
            "Tags": tags,
        },
    }

    asg_properties = definition["Resources"][asg_name]["Properties"]

    if "OperatorTopicId" in info:
        asg_properties["NotificationConfiguration"] = {
            "NotificationTypes": [
                "autoscaling:EC2_INSTANCE_LAUNCH",
                "autoscaling:EC2_INSTANCE_LAUNCH_ERROR",
                "autoscaling:EC2_INSTANCE_TERMINATE",
                "autoscaling:EC2_INSTANCE_TERMINATE_ERROR",
            ],
            "TopicARN":
            resolve_topic_arn(args.region, info["OperatorTopicId"]),
        }

    default_health_check_type = "EC2"

    if "ElasticLoadBalancer" in configuration:
        if isinstance(configuration["ElasticLoadBalancer"], str):
            asg_properties["LoadBalancerNames"] = [{
                "Ref":
                configuration["ElasticLoadBalancer"]
            }]
        elif isinstance(configuration["ElasticLoadBalancer"], list):
            asg_properties["LoadBalancerNames"] = [{
                "Ref": ref
            } for ref in configuration["ElasticLoadBalancer"]]
        # use ELB health check by default
        default_health_check_type = "ELB"
    if "ElasticLoadBalancerV2" in configuration:
        if isinstance(configuration["ElasticLoadBalancerV2"], str):
            asg_properties["TargetGroupARNs"] = [{
                "Ref":
                configuration["ElasticLoadBalancerV2"] + "TargetGroup"
            }]
        elif isinstance(configuration["ElasticLoadBalancerV2"], list):
            asg_properties["TargetGroupARNs"] = [{
                "Ref": ref + "TargetGroup"
            } for ref in configuration["ElasticLoadBalancerV2"]]
        # use ELB health check by default
        default_health_check_type = "ELB"

    asg_properties["HealthCheckType"] = configuration.get(
        "HealthCheckType", default_health_check_type)
    asg_properties["HealthCheckGracePeriod"] = configuration.get(
        "HealthCheckGracePeriod", 300)

    if "AutoScaling" in configuration:
        as_conf = configuration["AutoScaling"]
        asg_properties["MaxSize"] = as_conf["Maximum"]
        asg_properties["MinSize"] = as_conf["Minimum"]
        asg_properties["DesiredCapacity"] = max(
            int(as_conf["Minimum"]), int(as_conf.get("DesiredCapacity", 1)))

        default_scaling_adjustment = as_conf.get("ScalingAdjustment", 1)
        default_cooldown = as_conf.get("Cooldown", "60")

        # ScaleUp policy
        scale_up_name = asg_name + "ScaleUp"
        scale_up_adjustment = int(
            as_conf.get("ScaleUpAdjustment", default_scaling_adjustment))
        scale_up_cooldown = as_conf.get("ScaleUpCooldown", default_cooldown)

        definition["Resources"][scale_up_name] = create_autoscaling_policy(
            asg_name, scale_up_name, scale_up_adjustment, scale_up_cooldown,
            definition)

        # ScaleDown policy
        scale_down_name = asg_name + "ScaleDown"
        scale_down_adjustment = (-1) * int(
            as_conf.get("ScaleDownAdjustment", default_scaling_adjustment))
        scale_down_cooldown = as_conf.get("ScaleDownCooldown",
                                          default_cooldown)

        definition["Resources"][scale_down_name] = create_autoscaling_policy(
            asg_name,
            scale_down_name,
            scale_down_adjustment,
            scale_down_cooldown,
            definition,
        )

        if "MetricType" in as_conf:
            metric_type = as_conf["MetricType"]
            metricfns = {
                "CPU": metric_cpu,
                "NetworkIn": metric_network,
                "NetworkOut": metric_network,
            }
            # lowercase cpu is an acceptable metric, be compatible
            if metric_type.lower() not in map(lambda t: t.lower(),
                                              metricfns.keys()):
                raise click.UsageError(
                    'Auto scaling MetricType "{}" not supported.'.format(
                        metric_type))
            metricfn = metricfns[metric_type]
            definition = metricfn(asg_name, definition, as_conf, args, info,
                                  force)
    else:
        asg_properties["MaxSize"] = 1
        asg_properties["MinSize"] = 1

    for res in (config_name, asg_name):
        props = definition["Resources"][res]["Properties"]
        additional_cf_properties = ADDITIONAL_PROPERTIES.get(
            definition["Resources"][res]["Type"])
        properties_allowed_to_overwrite = (
            set(props.keys()) - SENZA_PROPERTIES) | additional_cf_properties
        for key in properties_allowed_to_overwrite:
            if key in configuration:
                props[key] = configuration[key]

    return definition
Esempio n. 21
0
def component_auto_scaling_group(definition, configuration, args, info, force, account_info):
    definition = ensure_keys(definition, "Resources")

    # launch configuration
    config_name = configuration["Name"] + "Config"
    definition["Resources"][config_name] = {
        "Type": "AWS::AutoScaling::LaunchConfiguration",
        "Properties": {
            "InstanceType": configuration["InstanceType"],
            "ImageId": {"Fn::FindInMap": ["Images", {"Ref": "AWS::Region"}, configuration["Image"]]},
            "AssociatePublicIpAddress": configuration.get('AssociatePublicIpAddress', False),
            "EbsOptimized": configuration.get('EbsOptimized', False)
        }
    }

    if 'IamRoles' in configuration:
        logical_id = handle_iam_roles(definition, configuration, args)
        definition["Resources"][config_name]["Properties"]["IamInstanceProfile"] = {'Ref': logical_id}

    if "SecurityGroups" in configuration:
        definition["Resources"][config_name]["Properties"]["SecurityGroups"] = \
            resolve_security_groups(configuration["SecurityGroups"], args.region)

    if "UserData" in configuration:
        definition["Resources"][config_name]["Properties"]["UserData"] = {
            "Fn::Base64": configuration["UserData"]
        }

    # auto scaling group
    asg_name = configuration["Name"]
    asg_success = ["1", "PT15M"]
    if "AutoScaling" in configuration:
        if "SuccessRequires" in configuration["AutoScaling"]:
            asg_success = normalize_asg_success(configuration["AutoScaling"]["SuccessRequires"])

    tags = [
        # Tag "Name"
        {
            "Key": "Name",
            "PropagateAtLaunch": True,
            "Value": "{0}-{1}".format(info["StackName"], info["StackVersion"])
        },
        # Tag "StackName"
        {
            "Key": "StackName",
            "PropagateAtLaunch": True,
            "Value": info["StackName"],
        },
        # Tag "StackVersion"
        {
            "Key": "StackVersion",
            "PropagateAtLaunch": True,
            "Value": info["StackVersion"]
        }
    ]

    if "Tags" in configuration:
        for tag in configuration["Tags"]:
            tags.append({
                "Key": tag["Key"],
                "PropagateAtLaunch": True,
                "Value": tag["Value"]
            })

    definition["Resources"][asg_name] = {
        "Type": "AWS::AutoScaling::AutoScalingGroup",
        # wait to get a signal from an amount of servers to signal that it booted
        "CreationPolicy": {
            "ResourceSignal": {
                "Count": asg_success[0],
                "Timeout": asg_success[1]
            }
        },
        "Properties": {
            # for our operator some notifications
            "LaunchConfigurationName": {"Ref": config_name},
            "VPCZoneIdentifier": {"Fn::FindInMap": ["ServerSubnets", {"Ref": "AWS::Region"}, "Subnets"]},
            "Tags": tags
        }
    }

    asg_properties = definition["Resources"][asg_name]["Properties"]

    if "OperatorTopicId" in info:
        asg_properties["NotificationConfiguration"] = {
            "NotificationTypes": [
                "autoscaling:EC2_INSTANCE_LAUNCH",
                "autoscaling:EC2_INSTANCE_LAUNCH_ERROR",
                "autoscaling:EC2_INSTANCE_TERMINATE",
                "autoscaling:EC2_INSTANCE_TERMINATE_ERROR"
            ],
            "TopicARN": resolve_topic_arn(args.region, info["OperatorTopicId"])
        }

    default_health_check_type = 'EC2'

    if "ElasticLoadBalancer" in configuration:
        if isinstance(configuration["ElasticLoadBalancer"], str):
            asg_properties["LoadBalancerNames"] = [{"Ref": configuration["ElasticLoadBalancer"]}]
        elif isinstance(configuration["ElasticLoadBalancer"], list):
            asg_properties["LoadBalancerNames"] = [{'Ref': ref} for ref in configuration["ElasticLoadBalancer"]]
        # use ELB health check by default
        default_health_check_type = 'ELB'
    if "ElasticLoadBalancerV2" in configuration:
        if isinstance(configuration["ElasticLoadBalancerV2"], str):
            asg_properties["TargetGroupARNs"] = [{"Ref": configuration["ElasticLoadBalancerV2"] + 'TargetGroup'}]
        elif isinstance(configuration["ElasticLoadBalancerV2"], list):
            asg_properties["TargetGroupARNs"] = [
                {'Ref': ref + 'TargetGroup'} for ref in configuration["ElasticLoadBalancerV2"]
            ]
        # use ELB health check by default
        default_health_check_type = 'ELB'

    asg_properties['HealthCheckType'] = configuration.get('HealthCheckType', default_health_check_type)
    asg_properties['HealthCheckGracePeriod'] = configuration.get('HealthCheckGracePeriod', 300)

    if "AutoScaling" in configuration:
        as_conf = configuration["AutoScaling"]
        asg_properties["MaxSize"] = as_conf["Maximum"]
        asg_properties["MinSize"] = as_conf["Minimum"]
        asg_properties["DesiredCapacity"] = max(int(as_conf["Minimum"]), int(as_conf.get('DesiredCapacity', 1)))

        default_scaling_adjustment = as_conf.get("ScalingAdjustment", 1)
        default_cooldown = as_conf.get("Cooldown", "60")

        # ScaleUp policy
        scale_up_name = asg_name + "ScaleUp"
        scale_up_adjustment = int(
            as_conf.get("ScaleUpAdjustment", default_scaling_adjustment))
        scale_up_cooldown = as_conf.get(
            "ScaleUpCooldown", default_cooldown)

        definition["Resources"][scale_up_name] = create_autoscaling_policy(
            asg_name, scale_up_name, scale_up_adjustment, scale_up_cooldown, definition)

        # ScaleDown policy
        scale_down_name = asg_name + "ScaleDown"
        scale_down_adjustment = (-1) * int(
            as_conf.get("ScaleDownAdjustment", default_scaling_adjustment))
        scale_down_cooldown = as_conf.get(
            "ScaleDownCooldown", default_cooldown)

        definition["Resources"][scale_down_name] = create_autoscaling_policy(
            asg_name, scale_down_name, scale_down_adjustment, scale_down_cooldown, definition)

        if "MetricType" in as_conf:
            metric_type = as_conf["MetricType"]
            metricfns = {
                "CPU": metric_cpu,
                "NetworkIn": metric_network,
                "NetworkOut": metric_network
            }
            # lowercase cpu is an acceptable metric, be compatible
            if metric_type.lower() not in map(lambda t: t.lower(), metricfns.keys()):
                raise click.UsageError('Auto scaling MetricType "{}" not supported.'.format(metric_type))
            metricfn = metricfns[metric_type]
            definition = metricfn(asg_name, definition, as_conf, args, info, force)
    else:
        asg_properties["MaxSize"] = 1
        asg_properties["MinSize"] = 1

    for res in (config_name, asg_name):
        props = definition['Resources'][res]['Properties']
        additional_cf_properties = ADDITIONAL_PROPERTIES.get(definition['Resources'][res]['Type'])
        properties_allowed_to_overwrite = (set(props.keys()) - SENZA_PROPERTIES) | additional_cf_properties
        for key in properties_allowed_to_overwrite:
            if key in configuration:
                props[key] = configuration[key]

    return definition
Esempio n. 22
0
def component_auto_scaling_group(definition, configuration, args, info, force,
                                 account_info):
    definition = ensure_keys(definition, "Resources")

    # launch configuration
    config_name = configuration["Name"] + "Config"
    definition["Resources"][config_name] = {
        "Type": "AWS::AutoScaling::LaunchConfiguration",
        "Properties": {
            "InstanceType":
            configuration["InstanceType"],
            "ImageId": {
                "Fn::FindInMap":
                ["Images", {
                    "Ref": "AWS::Region"
                }, configuration["Image"]]
            },
            "AssociatePublicIpAddress":
            configuration.get('AssociatePublicIpAddress', False),
            "EbsOptimized":
            configuration.get('EbsOptimized', False)
        }
    }

    if 'IamRoles' in configuration:
        logical_id = configuration['Name'] + 'InstanceProfile'
        roles = configuration['IamRoles']
        if len(roles) > 1:
            for role in roles:
                if isinstance(role, dict):
                    raise click.UsageError(
                        'Cannot merge policies of Cloud Formation references ({"Ref": ".."}): '
                        + 'You can use at most one IAM role with "Ref".')
            logical_role_id = configuration['Name'] + 'Role'
            definition['Resources'][logical_role_id] = {
                'Type': 'AWS::IAM::Role',
                'Properties': {
                    "AssumeRolePolicyDocument": {
                        "Version":
                        "2012-10-17",
                        "Statement": [{
                            "Effect": "Allow",
                            "Principal": {
                                "Service": ["ec2.amazonaws.com"]
                            },
                            "Action": ["sts:AssumeRole"]
                        }]
                    },
                    'Path': '/',
                    'Policies': get_merged_policies(roles)
                }
            }
            instance_profile_roles = [{'Ref': logical_role_id}]
        elif isinstance(roles[0], dict):
            instance_profile_roles = [
                resolve_referenced_resource(roles[0], args.region)
            ]
        else:
            instance_profile_roles = roles
        definition['Resources'][logical_id] = {
            'Type': 'AWS::IAM::InstanceProfile',
            'Properties': {
                'Path': '/',
                'Roles': instance_profile_roles
            }
        }
        definition["Resources"][config_name]["Properties"][
            "IamInstanceProfile"] = {
                'Ref': logical_id
            }

    if "SecurityGroups" in configuration:
        definition["Resources"][config_name]["Properties"]["SecurityGroups"] = \
            resolve_security_groups(configuration["SecurityGroups"], args.region)

    if "UserData" in configuration:
        definition["Resources"][config_name]["Properties"]["UserData"] = {
            "Fn::Base64": configuration["UserData"]
        }

    # auto scaling group
    asg_name = configuration["Name"]
    asg_success = ["1", "PT15M"]
    if "AutoScaling" in configuration:
        if "SuccessRequires" in configuration["AutoScaling"]:
            asg_success = normalize_asg_success(
                configuration["AutoScaling"]["SuccessRequires"])

    tags = [
        # Tag "Name"
        {
            "Key": "Name",
            "PropagateAtLaunch": True,
            "Value": "{0}-{1}".format(info["StackName"], info["StackVersion"])
        },
        # Tag "StackName"
        {
            "Key": "StackName",
            "PropagateAtLaunch": True,
            "Value": info["StackName"],
        },
        # Tag "StackVersion"
        {
            "Key": "StackVersion",
            "PropagateAtLaunch": True,
            "Value": info["StackVersion"]
        }
    ]

    if "Tags" in configuration:
        for tag in configuration["Tags"]:
            tags.append({
                "Key": tag["Key"],
                "PropagateAtLaunch": True,
                "Value": tag["Value"]
            })

    definition["Resources"][asg_name] = {
        "Type": "AWS::AutoScaling::AutoScalingGroup",
        # wait to get a signal from an amount of servers to signal that it booted
        "CreationPolicy": {
            "ResourceSignal": {
                "Count": asg_success[0],
                "Timeout": asg_success[1]
            }
        },
        "Properties": {
            # for our operator some notifications
            "LaunchConfigurationName": {
                "Ref": config_name
            },
            "VPCZoneIdentifier": {
                "Fn::FindInMap":
                ["ServerSubnets", {
                    "Ref": "AWS::Region"
                }, "Subnets"]
            },
            "Tags": tags
        }
    }

    asg_properties = definition["Resources"][asg_name]["Properties"]

    if "OperatorTopicId" in info:
        asg_properties["NotificationConfiguration"] = {
            "NotificationTypes": [
                "autoscaling:EC2_INSTANCE_LAUNCH",
                "autoscaling:EC2_INSTANCE_LAUNCH_ERROR",
                "autoscaling:EC2_INSTANCE_TERMINATE",
                "autoscaling:EC2_INSTANCE_TERMINATE_ERROR"
            ],
            "TopicARN":
            resolve_topic_arn(args.region, info["OperatorTopicId"])
        }

    default_health_check_type = 'EC2'

    if "ElasticLoadBalancer" in configuration:
        if isinstance(configuration["ElasticLoadBalancer"], str):
            asg_properties["LoadBalancerNames"] = [{
                "Ref":
                configuration["ElasticLoadBalancer"]
            }]
        elif isinstance(configuration["ElasticLoadBalancer"], list):
            asg_properties["LoadBalancerNames"] = [{
                'Ref': ref
            } for ref in configuration["ElasticLoadBalancer"]]
        # use ELB health check by default
        default_health_check_type = 'ELB'
    if "ElasticLoadBalancerV2" in configuration:
        if isinstance(configuration["ElasticLoadBalancerV2"], str):
            asg_properties["TargetGroupARNs"] = [{
                "Ref":
                configuration["ElasticLoadBalancerV2"] + 'TargetGroup'
            }]
        elif isinstance(configuration["ElasticLoadBalancerV2"], list):
            asg_properties["TargetGroupARNs"] = [{
                'Ref': ref + 'TargetGroup'
            } for ref in configuration["ElasticLoadBalancerV2"]]
        # use ELB health check by default
        default_health_check_type = 'ELB'

    asg_properties['HealthCheckType'] = configuration.get(
        'HealthCheckType', default_health_check_type)
    asg_properties['HealthCheckGracePeriod'] = configuration.get(
        'HealthCheckGracePeriod', 300)

    if "AutoScaling" in configuration:
        as_conf = configuration["AutoScaling"]
        asg_properties["MaxSize"] = as_conf["Maximum"]
        asg_properties["MinSize"] = as_conf["Minimum"]
        asg_properties["DesiredCapacity"] = max(
            int(as_conf["Minimum"]), int(as_conf.get('DesiredCapacity', 1)))

        default_scaling_adjustment = as_conf.get("ScalingAdjustment", 1)
        default_cooldown = as_conf.get("Cooldown", "60")

        # ScaleUp policy
        scale_up_name = asg_name + "ScaleUp"
        scale_up_adjustment = int(
            as_conf.get("ScaleUpAdjustment", default_scaling_adjustment))
        scale_up_cooldown = as_conf.get("ScaleUpCooldown", default_cooldown)

        definition["Resources"][scale_up_name] = create_autoscaling_policy(
            asg_name, scale_up_name, scale_up_adjustment, scale_up_cooldown,
            definition)

        # ScaleDown policy
        scale_down_name = asg_name + "ScaleDown"
        scale_down_adjustment = (-1) * int(
            as_conf.get("ScaleDownAdjustment", default_scaling_adjustment))
        scale_down_cooldown = as_conf.get("ScaleDownCooldown",
                                          default_cooldown)

        definition["Resources"][scale_down_name] = create_autoscaling_policy(
            asg_name, scale_down_name, scale_down_adjustment,
            scale_down_cooldown, definition)

        if "MetricType" in as_conf:
            metric_type = as_conf["MetricType"]
            metricfns = {
                "CPU": metric_cpu,
                "NetworkIn": metric_network,
                "NetworkOut": metric_network
            }
            # lowercase cpu is an acceptable metric, be compatible
            if metric_type.lower() not in map(lambda t: t.lower(),
                                              metricfns.keys()):
                raise click.UsageError(
                    'Auto scaling MetricType "{}" not supported.'.format(
                        metric_type))
            metricfn = metricfns[metric_type]
            definition = metricfn(asg_name, definition, as_conf, args, info,
                                  force)
    else:
        asg_properties["MaxSize"] = 1
        asg_properties["MinSize"] = 1

    for res in (config_name, asg_name):
        props = definition['Resources'][res]['Properties']
        additional_cf_properties = ADDITIONAL_PROPERTIES.get(
            definition['Resources'][res]['Type'])
        properties_allowed_to_overwrite = (
            set(props.keys()) - SENZA_PROPERTIES) | additional_cf_properties
        for key in properties_allowed_to_overwrite:
            if key in configuration:
                props[key] = configuration[key]

    return definition
Esempio n. 23
0
def component_elastic_load_balancer(definition, configuration, args, info, force):
    lb_name = configuration["Name"]

    # domains pointing to the load balancer
    main_zone = None
    for name, domain in configuration.get('Domains', {}).items():
        definition["Resources"][name] = {
            "Type": "AWS::Route53::RecordSet",
            "Properties": {
                "Type": "CNAME",
                "TTL": 20,
                "ResourceRecords": [
                    {"Fn::GetAtt": [lb_name, "DNSName"]}
                ],
                "Name": "{0}.{1}".format(domain["Subdomain"], domain["Zone"]),
                "HostedZoneName": "{0}.".format(domain["Zone"])
            },
        }

        if domain["Type"] == "weighted":
            definition["Resources"][name]["Properties"]['Weight'] = 0
            definition["Resources"][name]["Properties"]['SetIdentifier'] = "{0}-{1}".format(info["StackName"],
                                                                                            info["StackVersion"])
            main_zone = domain['Zone']

    ssl_cert = configuration.get('SSLCertificateId')

    pattern = None
    if not ssl_cert:
        if main_zone:
            pattern = main_zone.lower().replace('.', '-')
        else:
            pattern = ''
    elif not ssl_cert.startswith('arn:'):
        pattern = ssl_cert

    if pattern is not None:
        ssl_cert = find_ssl_certificate_arn(args.region, pattern)

        if not ssl_cert:
            raise click.UsageError('Could not find any matching SSL certificate for "{}"'.format(pattern))

    health_check_protocol = "HTTP"
    allowed_health_check_protocols = ("HTTP", "TCP", "UDP", "SSL")
    if "HealthCheckProtocol" in configuration:
        health_check_protocol = configuration["HealthCheckProtocol"]

    if health_check_protocol not in allowed_health_check_protocols:
        raise click.UsageError('Protocol "{}" is not supported for LoadBalancer'.format(health_check_protocol))

    health_check_path = "/ui/"
    if "HealthCheckPath" in configuration:
        health_check_path = configuration["HealthCheckPath"]

    health_check_port = configuration["HTTPPort"]
    if "HealthCheckPort" in configuration:
        health_check_port = configuration["HealthCheckPort"]

    health_check_target = "{0}:{1}{2}".format(health_check_protocol, health_check_port, health_check_path)
    loadbalancer_name = get_load_balancer_name(info["StackName"], info["StackVersion"])

    loadbalancer_scheme = "internal"
    allowed_loadbalancer_schemes = ("internet-facing", "internal")
    if "Scheme" in configuration:
        loadbalancer_scheme = configuration["Scheme"]
    else:
        configuration["Scheme"] = loadbalancer_scheme

    if loadbalancer_scheme == 'internet-facing':
        click.secho('You are deploying an internet-facing ELB that will be publicly accessible! ' +
                    'You should have OAUTH2 and HTTPS in place!', fg='red', bold=True, err=True)

    if loadbalancer_scheme not in allowed_loadbalancer_schemes:
        raise click.UsageError('Scheme "{}" is not supported for LoadBalancer'.format(loadbalancer_scheme))

    if loadbalancer_scheme == "internal":
        loadbalancer_subnet_map = "LoadBalancerInternalSubnets"
    else:
        loadbalancer_subnet_map = "LoadBalancerSubnets"

    # load balancer
    definition["Resources"][lb_name] = {
        "Type": "AWS::ElasticLoadBalancing::LoadBalancer",
        "Properties": {
            "Subnets": {"Fn::FindInMap": [loadbalancer_subnet_map, {"Ref": "AWS::Region"}, "Subnets"]},
            "HealthCheck": {
                "HealthyThreshold": "2",
                "UnhealthyThreshold": "2",
                "Interval": "10",
                "Timeout": "5",
                "Target": health_check_target
            },
            "Listeners": [
                {
                    "PolicyNames": [],
                    "SSLCertificateId": ssl_cert,
                    "Protocol": "HTTPS",
                    "InstancePort": configuration["HTTPPort"],
                    "LoadBalancerPort": 443
                }
            ],
            "CrossZone": "true",
            "LoadBalancerName": loadbalancer_name,
            "SecurityGroups": resolve_security_groups(configuration["SecurityGroups"], args.region),
            "Tags": [
                # Tag "Name"
                {
                    "Key": "Name",
                    "Value": "{0}-{1}".format(info["StackName"], info["StackVersion"])
                },
                # Tag "StackName"
                {
                    "Key": "StackName",
                    "Value": info["StackName"],
                },
                # Tag "StackVersion"
                {
                    "Key": "StackVersion",
                    "Value": info["StackVersion"]
                }
            ]
        }
    }
    for key, val in configuration.items():
        # overwrite any specified properties, but
        # ignore our special Senza properties as they are not supported by CF
        if key not in SENZA_PROPERTIES:
            definition['Resources'][lb_name]['Properties'][key] = val

    return definition
Esempio n. 24
0
def component_elastic_load_balancer(definition, configuration, args, info, force):
    lb_name = configuration["Name"]

    # domains pointing to the load balancer
    main_zone = None
    for name, domain in configuration.get('Domains', {}).items():
        definition["Resources"][name] = {
            "Type": "AWS::Route53::RecordSet",
            "Properties": {
                "Type": "CNAME",
                "TTL": 20,
                "ResourceRecords": [
                    {"Fn::GetAtt": [lb_name, "DNSName"]}
                ],
                "Name": "{0}.{1}".format(domain["Subdomain"], domain["Zone"]),
                "HostedZoneName": "{0}.".format(domain["Zone"])
            },
        }

        if domain["Type"] == "weighted":
            definition["Resources"][name]["Properties"]['Weight'] = 0
            definition["Resources"][name]["Properties"]['SetIdentifier'] = "{0}-{1}".format(info["StackName"],
                                                                                            info["StackVersion"])
            main_zone = domain['Zone']

    ssl_cert = configuration.get('SSLCertificateId')

    pattern = None
    if not ssl_cert:
        if main_zone:
            pattern = main_zone.lower().replace('.', '-')
        else:
            pattern = ''
    elif not ssl_cert.startswith('arn:'):
        pattern = ssl_cert

    if pattern is not None:
        ssl_cert = find_ssl_certificate_arn(args.region, pattern)

        if not ssl_cert:
            raise click.UsageError('Could not find any matching SSL certificate for "{}"'.format(pattern))

    health_check_protocol = "HTTP"
    allowed_health_check_protocols = ("HTTP", "TCP", "UDP", "SSL")
    if "HealthCheckProtocol" in configuration:
        health_check_protocol = configuration["HealthCheckProtocol"]

    if health_check_protocol not in allowed_health_check_protocols:
        raise click.UsageError('Protocol "{}" is not supported for LoadBalancer'.format(health_check_protocol))

    health_check_path = "/ui/"
    if "HealthCheckPath" in configuration:
        health_check_path = configuration["HealthCheckPath"]

    health_check_target = "{0}:{1}{2}".format(health_check_protocol, configuration["HTTPPort"], health_check_path)

    # load balancer
    definition["Resources"][lb_name] = {
        "Type": "AWS::ElasticLoadBalancing::LoadBalancer",
        "Properties": {
            "Scheme": "internet-facing",
            "Subnets": {"Fn::FindInMap": ["LoadBalancerSubnets", {"Ref": "AWS::Region"}, "Subnets"]},
            "HealthCheck": {
                "HealthyThreshold": "2",
                "UnhealthyThreshold": "2",
                "Interval": "10",
                "Timeout": "5",
                "Target": health_check_target
            },
            "Listeners": [
                {
                    "PolicyNames": [],
                    "SSLCertificateId": ssl_cert,
                    "Protocol": "HTTPS",
                    "InstancePort": configuration["HTTPPort"],
                    "LoadBalancerPort": 443
                }
            ],
            "CrossZone": "true",
            "LoadBalancerName": "{0}-{1}".format(info["StackName"], info["StackVersion"]),
            "SecurityGroups": resolve_security_groups(configuration["SecurityGroups"], args.region),
            "Tags": [
                # Tag "Name"
                {
                    "Key": "Name",
                    "Value": "{0}-{1}".format(info["StackName"], info["StackVersion"])
                },
                # Tag "StackName"
                {
                    "Key": "StackName",
                    "Value": info["StackName"],
                },
                # Tag "StackVersion"
                {
                    "Key": "StackVersion",
                    "Value": info["StackVersion"]
                }
            ]
        }
    }

    return definition
Esempio n. 25
0
def component_auto_scaling_group(definition, configuration, args, info, force, account_info):
    definition = ensure_keys(definition, "Resources")

    # launch configuration
    config_name = configuration["Name"] + "Config"
    definition["Resources"][config_name] = {
        "Type": "AWS::AutoScaling::LaunchConfiguration",
        "Properties": {
            "InstanceType": configuration["InstanceType"],
            "ImageId": {"Fn::FindInMap": ["Images", {"Ref": "AWS::Region"}, configuration["Image"]]},
            "AssociatePublicIpAddress": configuration.get('AssociatePublicIpAddress', False),
            "EbsOptimized": configuration.get('EbsOptimized', False)
        }
    }

    if 'BlockDeviceMappings' in configuration:
        definition['Resources'][config_name]['Properties']['BlockDeviceMappings'] = configuration['BlockDeviceMappings']

    if "IamInstanceProfile" in configuration:
        definition["Resources"][config_name]["Properties"]["IamInstanceProfile"] = configuration["IamInstanceProfile"]

    if 'IamRoles' in configuration:
        logical_id = configuration['Name'] + 'InstanceProfile'
        roles = configuration['IamRoles']
        if len(roles) > 1:
            for role in roles:
                if isinstance(role, dict):
                    raise click.UsageError('Cannot merge policies of Cloud Formation references ({"Ref": ".."}): ' +
                                           'You can use at most one IAM role with "Ref".')
            logical_role_id = configuration['Name'] + 'Role'
            definition['Resources'][logical_role_id] = {
                'Type': 'AWS::IAM::Role',
                'Properties': {
                    "AssumeRolePolicyDocument": {
                        "Version": "2012-10-17",
                        "Statement": [
                            {
                                "Effect": "Allow",
                                "Principal": {
                                    "Service": ["ec2.amazonaws.com"]
                                },
                                "Action": ["sts:AssumeRole"]
                            }
                        ]
                    },
                    'Path': '/',
                    'Policies': get_merged_policies(roles)
                }
            }
            instance_profile_roles = [{'Ref': logical_role_id}]
        elif isinstance(roles[0], dict):
            instance_profile_roles = [resolve_referenced_resource(roles[0], args.region)]
        else:
            instance_profile_roles = roles
        definition['Resources'][logical_id] = {
            'Type': 'AWS::IAM::InstanceProfile',
            'Properties': {
                'Path': '/',
                'Roles': instance_profile_roles
            }
        }
        definition["Resources"][config_name]["Properties"]["IamInstanceProfile"] = {'Ref': logical_id}

    if "SecurityGroups" in configuration:
        definition["Resources"][config_name]["Properties"]["SecurityGroups"] = \
            resolve_security_groups(configuration["SecurityGroups"], args.region)

    if "UserData" in configuration:
        definition["Resources"][config_name]["Properties"]["UserData"] = {
            "Fn::Base64": configuration["UserData"]
        }

    # auto scaling group
    asg_name = configuration["Name"]
    asg_success = ["1", "PT15M"]
    if "AutoScaling" in configuration:
        if "SuccessRequires" in configuration["AutoScaling"]:
            asg_success = normalize_asg_success(configuration["AutoScaling"]["SuccessRequires"])

    definition["Resources"][asg_name] = {
        "Type": "AWS::AutoScaling::AutoScalingGroup",
        # wait to get a signal from an amount of servers to signal that it booted
        "CreationPolicy": {
            "ResourceSignal": {
                "Count": asg_success[0],
                "Timeout": asg_success[1]
            }
        },
        "Properties": {
            # for our operator some notifications
            "LaunchConfigurationName": {"Ref": config_name},
            "VPCZoneIdentifier": {"Fn::FindInMap": ["ServerSubnets", {"Ref": "AWS::Region"}, "Subnets"]},
            "Tags": [
                # Tag "Name"
                {
                    "Key": "Name",
                    "PropagateAtLaunch": True,
                    "Value": "{0}-{1}".format(info["StackName"], info["StackVersion"])
                },
                # Tag "StackName"
                {
                    "Key": "StackName",
                    "PropagateAtLaunch": True,
                    "Value": info["StackName"],
                },
                # Tag "StackVersion"
                {
                    "Key": "StackVersion",
                    "PropagateAtLaunch": True,
                    "Value": info["StackVersion"]
                }
            ]
        }
    }

    asg_properties = definition["Resources"][asg_name]["Properties"]

    if "OperatorTopicId" in info:
        asg_properties["NotificationConfiguration"] = {
            "NotificationTypes": [
                "autoscaling:EC2_INSTANCE_LAUNCH",
                "autoscaling:EC2_INSTANCE_LAUNCH_ERROR",
                "autoscaling:EC2_INSTANCE_TERMINATE",
                "autoscaling:EC2_INSTANCE_TERMINATE_ERROR"
            ],
            "TopicARN": resolve_topic_arn(args.region, info["OperatorTopicId"])
        }

    default_health_check_type = 'EC2'

    if "ElasticLoadBalancer" in configuration:
        if isinstance(configuration["ElasticLoadBalancer"], str):
            asg_properties["LoadBalancerNames"] = [{"Ref": configuration["ElasticLoadBalancer"]}]
        elif isinstance(configuration["ElasticLoadBalancer"], list):
            asg_properties["LoadBalancerNames"] = [{'Ref': ref} for ref in configuration["ElasticLoadBalancer"]]
        # use ELB health check by default
        default_health_check_type = 'ELB'

    asg_properties['HealthCheckType'] = configuration.get('HealthCheckType', default_health_check_type)
    asg_properties['HealthCheckGracePeriod'] = configuration.get('HealthCheckGracePeriod', 300)

    if "AutoScaling" in configuration:
        as_conf = configuration["AutoScaling"]
        asg_properties["MaxSize"] = as_conf["Maximum"]
        asg_properties["MinSize"] = as_conf["Minimum"]
        asg_properties["DesiredCapacity"] = max(int(as_conf["Minimum"]), int(as_conf.get('DesiredCapacity', 1)))

        scaling_adjustment = int(as_conf.get("ScalingAdjustment", 1))
        # ScaleUp policy
        definition["Resources"][asg_name + "ScaleUp"] = {
            "Type": "AWS::AutoScaling::ScalingPolicy",
            "Properties": {
                "AdjustmentType": "ChangeInCapacity",
                "ScalingAdjustment": str(scaling_adjustment),
                "Cooldown": str(as_conf.get("Cooldown", "60")),
                "AutoScalingGroupName": {
                    "Ref": asg_name
                }
            }
        }

        # ScaleDown policy
        definition["Resources"][asg_name + "ScaleDown"] = {
            "Type": "AWS::AutoScaling::ScalingPolicy",
            "Properties": {
                "AdjustmentType": "ChangeInCapacity",
                "ScalingAdjustment": str((-1) * scaling_adjustment),
                "Cooldown": str(as_conf.get("Cooldown", "60")),
                "AutoScalingGroupName": {
                    "Ref": asg_name
                }
            }
        }

        if "MetricType" in as_conf:
            metric_type = as_conf["MetricType"]
            metricfns = {
                "CPU": metric_cpu,
                "NetworkIn": metric_network,
                "NetworkOut": metric_network
            }
            # lowercase cpu is an acceptable metric, be compatible
            if metric_type.lower() not in map(lambda t: t.lower(), metricfns.keys()):
                raise click.UsageError('Auto scaling MetricType "{}" not supported.'.format(metric_type))
            metricfn = metricfns[metric_type]
            definition = metricfn(asg_name, definition, as_conf, args, info, force)
    else:
        asg_properties["MaxSize"] = 1
        asg_properties["MinSize"] = 1

    return definition
Esempio n. 26
0
def component_auto_scaling_group(definition, configuration, args, info, force):
    definition = ensure_keys(definition, "Resources")

    # launch configuration
    config_name = configuration["Name"] + "Config"
    definition["Resources"][config_name] = {
        "Type": "AWS::AutoScaling::LaunchConfiguration",
        "Properties": {
            "InstanceType": configuration["InstanceType"],
            "ImageId": {"Fn::FindInMap": ["Images", {"Ref": "AWS::Region"}, configuration["Image"]]},
            "AssociatePublicIpAddress": configuration.get('AssociatePublicIpAddress', False),
            "EbsOptimized": configuration.get('EbsOptimized', False)
        }
    }

    if 'BlockDeviceMappings' in configuration:
        definition['Resources'][config_name]['Properties']['BlockDeviceMappings'] = configuration['BlockDeviceMappings']

    if "IamInstanceProfile" in configuration:
        definition["Resources"][config_name]["Properties"]["IamInstanceProfile"] = configuration["IamInstanceProfile"]

    if 'IamRoles' in configuration:
        logical_id = configuration['Name'] + 'InstanceProfile'
        roles = configuration['IamRoles']
        if len(roles) > 1:
            for role in roles:
                if isinstance(role, dict):
                    raise click.UsageError('Cannot merge policies of Cloud Formation references ({"Ref": ".."}): ' +
                                           'You can use at most one IAM role with "Ref".')
            logical_role_id = configuration['Name'] + 'Role'
            definition['Resources'][logical_role_id] = {
                'Type': 'AWS::IAM::Role',
                'Properties': {
                    "AssumeRolePolicyDocument": {
                        "Version": "2012-10-17",
                        "Statement": [
                            {
                                "Effect": "Allow",
                                "Principal": {
                                    "Service": ["ec2.amazonaws.com"]
                                },
                                "Action": ["sts:AssumeRole"]
                            }
                        ]
                    },
                    'Path': '/',
                    'Policies': get_merged_policies(roles, args.region)
                }
            }
            instance_profile_roles = [{'Ref': logical_role_id}]
        else:
            instance_profile_roles = roles
        definition['Resources'][logical_id] = {
            'Type': 'AWS::IAM::InstanceProfile',
            'Properties': {
                'Path': '/',
                'Roles': instance_profile_roles
            }
        }
        definition["Resources"][config_name]["Properties"]["IamInstanceProfile"] = {'Ref': logical_id}

    if "SecurityGroups" in configuration:
        definition["Resources"][config_name]["Properties"]["SecurityGroups"] = \
            resolve_security_groups(configuration["SecurityGroups"], args.region)

    if "UserData" in configuration:
        definition["Resources"][config_name]["Properties"]["UserData"] = {
            "Fn::Base64": configuration["UserData"]
        }

    # auto scaling group
    asg_name = configuration["Name"]
    definition["Resources"][asg_name] = {
        "Type": "AWS::AutoScaling::AutoScalingGroup",
        # wait up to 15 minutes to get a signal from at least one server that it booted
        "CreationPolicy": {
            "ResourceSignal": {
                "Count": "1",
                "Timeout": "PT15M"
            }
        },
        "Properties": {
            # for our operator some notifications
            "LaunchConfigurationName": {"Ref": config_name},
            "VPCZoneIdentifier": {"Fn::FindInMap": ["ServerSubnets", {"Ref": "AWS::Region"}, "Subnets"]},
            "Tags": [
                # Tag "Name"
                {
                    "Key": "Name",
                    "PropagateAtLaunch": True,
                    "Value": "{0}-{1}".format(info["StackName"], info["StackVersion"])
                },
                # Tag "StackName"
                {
                    "Key": "StackName",
                    "PropagateAtLaunch": True,
                    "Value": info["StackName"],
                },
                # Tag "StackVersion"
                {
                    "Key": "StackVersion",
                    "PropagateAtLaunch": True,
                    "Value": info["StackVersion"]
                }
            ]
        }
    }

    if "OperatorTopicId" in info:
        definition["Resources"][asg_name]["Properties"]["NotificationConfiguration"] = {
            "NotificationTypes": [
                "autoscaling:EC2_INSTANCE_LAUNCH",
                "autoscaling:EC2_INSTANCE_LAUNCH_ERROR",
                "autoscaling:EC2_INSTANCE_TERMINATE",
                "autoscaling:EC2_INSTANCE_TERMINATE_ERROR"
            ],
            "TopicARN": resolve_topic_arn(args.region, info["OperatorTopicId"])
        }

    default_health_check_type = 'EC2'

    if "ElasticLoadBalancer" in configuration:
        definition["Resources"][asg_name]["Properties"]["LoadBalancerNames"] = [
            {"Ref": configuration["ElasticLoadBalancer"]}]
        # use ELB health check by default
        default_health_check_type = 'ELB'

    definition["Resources"][asg_name]['Properties']['HealthCheckType'] = \
        configuration.get('HealthCheckType', default_health_check_type)
    definition["Resources"][asg_name]['Properties']['HealthCheckGracePeriod'] = \
        configuration.get('HealthCheckGracePeriod', 300)

    if "AutoScaling" in configuration:
        definition["Resources"][asg_name]["Properties"]["MaxSize"] = configuration["AutoScaling"]["Maximum"]
        definition["Resources"][asg_name]["Properties"]["MinSize"] = configuration["AutoScaling"]["Minimum"]

        # ScaleUp policy
        definition["Resources"][asg_name + "ScaleUp"] = {
            "Type": "AWS::AutoScaling::ScalingPolicy",
            "Properties": {
                "AdjustmentType": "ChangeInCapacity",
                "ScalingAdjustment": "1",
                "Cooldown": "60",
                "AutoScalingGroupName": {
                    "Ref": asg_name
                }
            }
        }

        # ScaleDown policy
        definition["Resources"][asg_name + "ScaleDown"] = {
            "Type": "AWS::AutoScaling::ScalingPolicy",
            "Properties": {
                "AdjustmentType": "ChangeInCapacity",
                "ScalingAdjustment": "-1",
                "Cooldown": "60",
                "AutoScalingGroupName": {
                    "Ref": asg_name
                }
            }
        }

        metric_type = configuration["AutoScaling"]["MetricType"]
        metricfn = globals().get('metric_{}'.format(metric_type.lower()))
        if not metricfn:
            raise click.UsageError('Auto scaling MetricType "{}" not supported.'.format(metric_type))
        definition = metricfn(asg_name, definition, configuration["AutoScaling"], args, info, force)
    else:
        definition["Resources"][asg_name]["Properties"]["MaxSize"] = 1
        definition["Resources"][asg_name]["Properties"]["MinSize"] = 1

    return definition