Example #1
0
 def format_network_configuration(self, network_config):
     result = dict()
     if network_config['subnets'] is not None:
         result['subnets'] = network_config['subnets']
     else:
         self.module.fail_json(
             msg="Network configuration must include subnets")
     if network_config['security_groups'] is not None:
         groups = network_config['security_groups']
         if any(not sg.startswith('sg-') for sg in groups):
             try:
                 vpc_id = self.ec2.describe_subnets(
                     SubnetIds=[result['subnets'][0]
                                ])['Subnets'][0]['VpcId']
                 groups = get_ec2_security_group_ids_from_names(
                     groups, self.ec2, vpc_id)
             except (botocore.exceptions.ClientError,
                     botocore.exceptions.BotoCoreError) as e:
                 self.module.fail_json_aws(
                     e, msg="Couldn't look up security groups")
         result['securityGroups'] = groups
     if network_config['assign_public_ip'] is not None:
         if self.module.botocore_at_least('1.8.4'):
             if network_config['assign_public_ip'] is True:
                 result['assignPublicIp'] = "ENABLED"
             else:
                 result['assignPublicIp'] = "DISABLED"
         else:
             self.module.fail_json(
                 msg=
                 'botocore needs to be version 1.8.4 or higher to use assign_public_ip in network_configuration'
             )
     return dict(awsvpcConfiguration=result)
def create_eni(connection, vpc_id, module):

    instance_id = module.params.get("instance_id")
    attached = module.params.get("attached")
    if instance_id == 'None':
        instance_id = None
    device_index = module.params.get("device_index")
    subnet_id = module.params.get('subnet_id')
    private_ip_address = module.params.get('private_ip_address')
    description = module.params.get('description')
    security_groups = get_ec2_security_group_ids_from_names(
        module.params.get('security_groups'),
        connection,
        vpc_id=vpc_id,
        boto3=False)
    secondary_private_ip_addresses = module.params.get(
        "secondary_private_ip_addresses")
    secondary_private_ip_address_count = module.params.get(
        "secondary_private_ip_address_count")
    changed = False

    try:
        eni = connection.create_network_interface(subnet_id,
                                                  private_ip_address,
                                                  description, security_groups)
        if attached and instance_id is not None:
            try:
                eni.attach(instance_id, device_index)
            except BotoServerError:
                eni.delete()
                raise
            # Wait to allow creation / attachment to finish
            wait_for_eni(eni, "attached")
            eni.update()

        if secondary_private_ip_address_count is not None:
            try:
                connection.assign_private_ip_addresses(
                    network_interface_id=eni.id,
                    secondary_private_ip_address_count=
                    secondary_private_ip_address_count)
            except BotoServerError:
                eni.delete()
                raise

        if secondary_private_ip_addresses is not None:
            try:
                connection.assign_private_ip_addresses(
                    network_interface_id=eni.id,
                    private_ip_addresses=secondary_private_ip_addresses)
            except BotoServerError:
                eni.delete()
                raise

        changed = True

    except BotoServerError as e:
        module.fail_json(msg=e.message)

    module.exit_json(changed=changed, interface=get_eni_info(eni))
Example #3
0
def ensure_present(client, module):
    name = module.params.get('name')
    subnets = module.params['subnets']
    groups = module.params['security_groups']
    wait = module.params.get('wait')
    cluster = get_cluster(client, module)
    try:
        ec2 = module.client('ec2')
        vpc_id = ec2.describe_subnets(
            SubnetIds=[subnets[0]])['Subnets'][0]['VpcId']
        groups = get_ec2_security_group_ids_from_names(groups, ec2, vpc_id)
    except (botocore.exceptions.BotoCoreError,
            botocore.exceptions.ClientError) as e:
        module.fail_json_aws(e, msg="Couldn't lookup security groups")

    if cluster:
        if set(cluster['resourcesVpcConfig']['subnetIds']) != set(subnets):
            module.fail_json(msg="Cannot modify subnets of existing cluster")
        if set(cluster['resourcesVpcConfig']['securityGroupIds']) != set(
                groups):
            module.fail_json(
                msg="Cannot modify security groups of existing cluster")
        if module.params.get('version') and module.params.get(
                'version') != cluster['version']:
            module.fail_json(msg="Cannot modify version of existing cluster")

        if wait:
            wait_until(client, module, 'cluster_active')
            # Ensure that fields that are only available for active clusters are
            # included in the returned value
            cluster = get_cluster(client, module)

        module.exit_json(changed=False, **camel_dict_to_snake_dict(cluster))

    if module.check_mode:
        module.exit_json(changed=True)
    try:
        params = dict(name=name,
                      roleArn=module.params['role_arn'],
                      resourcesVpcConfig=dict(subnetIds=subnets,
                                              securityGroupIds=groups),
                      clientRequestToken='ansible-create-%s' % name)
        if module.params['version']:
            params['version'] = module.params['version']
        cluster = client.create_cluster(**params)['cluster']
    except botocore.exceptions.EndpointConnectionError as e:
        module.fail_json(msg="Region %s is not supported by EKS" %
                         client.meta.region_name)
    except (botocore.exceptions.BotoCoreError,
            botocore.exceptions.ClientError) as e:
        module.fail_json_aws(e, msg="Couldn't create cluster %s" % name)

    if wait:
        wait_until(client, module, 'cluster_active')
        # Ensure that fields that are only available for active clusters are
        # included in the returned value
        cluster = get_cluster(client, module)

    module.exit_json(changed=True, **camel_dict_to_snake_dict(cluster))
Example #4
0
def create_eni(connection, vpc_id, module):

    instance_id = module.params.get("instance_id")
    attached = module.params.get("attached")
    if instance_id == 'None':
        instance_id = None
    device_index = module.params.get("device_index")
    subnet_id = module.params.get('subnet_id')
    private_ip_address = module.params.get('private_ip_address')
    description = module.params.get('description')
    security_groups = get_ec2_security_group_ids_from_names(module.params.get('security_groups'), connection, vpc_id=vpc_id, boto3=False)
    secondary_private_ip_addresses = module.params.get("secondary_private_ip_addresses")
    secondary_private_ip_address_count = module.params.get("secondary_private_ip_address_count")
    changed = False

    try:
        eni = find_eni(connection, module)
        if eni is None:
            eni = connection.create_network_interface(subnet_id, private_ip_address, description, security_groups)
            if attached == True and instance_id is not None:
                try:
                    eni.attach(instance_id, device_index)
                except BotoServerError:
                    eni.delete()
                    raise
                # Wait to allow creation / attachment to finish
                wait_for_eni(eni, "attached")
                eni.update()

            if secondary_private_ip_address_count is not None:
                try:
                    connection.assign_private_ip_addresses(network_interface_id=eni.id, secondary_private_ip_address_count=secondary_private_ip_address_count)
                except BotoServerError:
                    eni.delete()
                    raise

            if secondary_private_ip_addresses is not None:
                try:
                    connection.assign_private_ip_addresses(network_interface_id=eni.id, private_ip_addresses=secondary_private_ip_addresses)
                except BotoServerError:
                    eni.delete()
                    raise

            changed = True

    except BotoServerError as e:
        module.fail_json(msg=e.message)

    module.exit_json(changed=changed, interface=get_eni_info(eni))
Example #5
0
 def format_network_configuration(self, network_config):
     result = dict()
     if 'subnets' in network_config:
         result['subnets'] = network_config['subnets']
     else:
         self.module.fail_json(msg="Network configuration must include subnets")
     if 'security_groups' in network_config:
         groups = network_config['security_groups']
         if any(not sg.startswith('sg-') for sg in groups):
             try:
                 vpc_id = self.ec2.describe_subnets(SubnetIds=[result['subnets'][0]])['Subnets'][0]['VpcId']
                 groups = get_ec2_security_group_ids_from_names(groups, self.ec2, vpc_id)
             except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
                 self.module.fail_json_aws(e, msg="Couldn't look up security groups")
         result['securityGroups'] = groups
     return dict(awsvpcConfiguration=result)
Example #6
0
 def format_network_configuration(self, network_config):
     result = dict()
     if 'subnets' in network_config:
         result['subnets'] = network_config['subnets']
     else:
         self.module.fail_json(
             msg="Network configuration must include subnets")
     if 'security_groups' in network_config:
         groups = network_config['security_groups']
         if any(not sg.startswith('sg-') for sg in groups):
             try:
                 vpc_id = self.ec2.describe_subnets(
                     SubnetIds=[result['subnets'][0]
                                ])['Subnets'][0]['VpcId']
                 groups = get_ec2_security_group_ids_from_names(
                     groups, self.ec2, vpc_id)
             except (botocore.exceptions.ClientError,
                     botocore.exceptions.BotoCoreError) as e:
                 self.module.fail_json_aws(
                     e, msg="Couldn't look up security groups")
         result['securityGroups'] = groups
     return dict(awsvpcConfiguration=result)
def create_or_update_elb(connection, connection_ec2, module):
    """Create ELB or modify main attributes. json_exit here"""

    changed = False
    new_load_balancer = False
    params = dict()
    params['Name'] = module.params.get("name")
    params['Subnets'] = module.params.get("subnets")
    try:
        params['SecurityGroups'] = get_ec2_security_group_ids_from_names(
            module.params.get('security_groups'), connection_ec2, boto3=True)
    except ValueError as e:
        module.fail_json(msg=str(e), exception=traceback.format_exc())
    except ClientError as e:
        module.fail_json(msg=e.message,
                         exception=traceback.format_exc(),
                         **camel_dict_to_snake_dict(e.response))
    except NoCredentialsError as e:
        module.fail_json(msg="AWS authentication problem. " + e.message,
                         exception=traceback.format_exc())

    params['Scheme'] = module.params.get("scheme")
    if module.params.get("tags"):
        params['Tags'] = ansible_dict_to_boto3_tag_list(
            module.params.get("tags"))
    purge_tags = module.params.get("purge_tags")
    access_logs_enabled = module.params.get("access_logs_enabled")
    access_logs_s3_bucket = module.params.get("access_logs_s3_bucket")
    access_logs_s3_prefix = module.params.get("access_logs_s3_prefix")
    deletion_protection = module.params.get("deletion_protection")
    idle_timeout = module.params.get("idle_timeout")

    # Does the ELB currently exist?
    elb = get_elb(connection, module)

    if elb:
        # ELB exists so check subnets, security groups and tags match what has been passed

        # Subnets
        if set(_get_subnet_ids_from_subnet_list(
                elb['AvailabilityZones'])) != set(params['Subnets']):
            try:
                connection.set_subnets(LoadBalancerArn=elb['LoadBalancerArn'],
                                       Subnets=params['Subnets'])
            except ClientError as e:
                module.fail_json(msg=e.message,
                                 exception=traceback.format_exc(),
                                 **camel_dict_to_snake_dict(e.response))
            changed = True

        # Security Groups
        if set(elb['SecurityGroups']) != set(params['SecurityGroups']):
            try:
                connection.set_security_groups(
                    LoadBalancerArn=elb['LoadBalancerArn'],
                    SecurityGroups=params['SecurityGroups'])
            except ClientError as e:
                module.fail_json(msg=e.message,
                                 exception=traceback.format_exc(),
                                 **camel_dict_to_snake_dict(e.response))
            changed = True

        # Tags - only need to play with tags if tags parameter has been set to something
        if module.params.get("tags"):
            try:
                elb_tags = connection.describe_tags(
                    ResourceArns=[elb['LoadBalancerArn']
                                  ])['TagDescriptions'][0]['Tags']
            except ClientError as e:
                module.fail_json(msg=e.message,
                                 exception=traceback.format_exc(),
                                 **camel_dict_to_snake_dict(e.response))

            # Delete necessary tags
            tags_need_modify, tags_to_delete = compare_aws_tags(
                boto3_tag_list_to_ansible_dict(elb_tags),
                boto3_tag_list_to_ansible_dict(params['Tags']), purge_tags)
            if tags_to_delete:
                try:
                    connection.remove_tags(
                        ResourceArns=[elb['LoadBalancerArn']],
                        TagKeys=tags_to_delete)
                except ClientError as e:
                    module.fail_json(msg=e.message,
                                     exception=traceback.format_exc(),
                                     **camel_dict_to_snake_dict(e.response))
                changed = True

            # Add/update tags
            if tags_need_modify:
                try:
                    connection.add_tags(ResourceArns=[elb['LoadBalancerArn']],
                                        Tags=params['Tags'])
                except ClientError as e:
                    module.fail_json(msg=e.message,
                                     exception=traceback.format_exc(),
                                     **camel_dict_to_snake_dict(e.response))
                changed = True

    else:
        try:
            elb = connection.create_load_balancer(**params)['LoadBalancers'][0]
            changed = True
            new_load_balancer = True
        except ClientError as e:
            module.fail_json(msg=e.message,
                             exception=traceback.format_exc(),
                             **camel_dict_to_snake_dict(e.response))

        if module.params.get("wait"):
            status_achieved, new_elb = wait_for_status(connection, module,
                                                       elb['LoadBalancerArn'],
                                                       'active')

    # Now set ELB attributes. Use try statement here so we can remove the ELB if this stage fails
    update_attributes = []

    # Get current attributes
    current_elb_attributes = get_elb_attributes(connection, module,
                                                elb['LoadBalancerArn'])

    if access_logs_enabled and current_elb_attributes[
            'access_logs_s3_enabled'] != "true":
        update_attributes.append({
            'Key': 'access_logs.s3.enabled',
            'Value': "true"
        })
    if not access_logs_enabled and current_elb_attributes[
            'access_logs_s3_enabled'] != "false":
        update_attributes.append({
            'Key': 'access_logs.s3.enabled',
            'Value': 'false'
        })
    if access_logs_s3_bucket is not None and access_logs_s3_bucket != current_elb_attributes[
            'access_logs_s3_bucket']:
        update_attributes.append({
            'Key': 'access_logs.s3.bucket',
            'Value': access_logs_s3_bucket
        })
    if access_logs_s3_prefix is not None and access_logs_s3_prefix != current_elb_attributes[
            'access_logs_s3_prefix']:
        update_attributes.append({
            'Key': 'access_logs.s3.prefix',
            'Value': access_logs_s3_prefix
        })
    if deletion_protection and current_elb_attributes[
            'deletion_protection_enabled'] != "true":
        update_attributes.append({
            'Key': 'deletion_protection.enabled',
            'Value': "true"
        })
    if not deletion_protection and current_elb_attributes[
            'deletion_protection_enabled'] != "false":
        update_attributes.append({
            'Key': 'deletion_protection.enabled',
            'Value': "false"
        })
    if idle_timeout is not None and str(
            idle_timeout
    ) != current_elb_attributes['idle_timeout_timeout_seconds']:
        update_attributes.append({
            'Key': 'idle_timeout.timeout_seconds',
            'Value': str(idle_timeout)
        })

    if update_attributes:
        try:
            connection.modify_load_balancer_attributes(
                LoadBalancerArn=elb['LoadBalancerArn'],
                Attributes=update_attributes)
            changed = True
        except ClientError as e:
            # Something went wrong setting attributes. If this ELB was created during this task, delete it to leave a consistent state
            if new_load_balancer:
                connection.delete_load_balancer(
                    LoadBalancerArn=elb['LoadBalancerArn'])
            module.fail_json(msg=e.message,
                             exception=traceback.format_exc(),
                             **camel_dict_to_snake_dict(e.response))

    # Now, if required, set ELB listeners. Use try statement here so we can remove the ELB if this stage fails
    try:
        listener_changed = create_or_update_elb_listeners(
            connection, module, elb)
        if listener_changed:
            changed = True
    except ClientError as e:
        # Something went wrong setting listeners. If this ELB was created during this task, delete it to leave a consistent state
        if new_load_balancer:
            connection.delete_load_balancer(
                LoadBalancerArn=elb['LoadBalancerArn'])
        module.fail_json(msg=e.message,
                         exception=traceback.format_exc(),
                         **camel_dict_to_snake_dict(e.response))

    # Get the ELB again
    elb = get_elb(connection, module)

    # Get the ELB listeners again
    elb['listeners'] = get_elb_listeners(connection, module,
                                         elb['LoadBalancerArn'])

    # For each listener, get listener rules
    for listener in elb['listeners']:
        listener['rules'] = get_listener_rules(connection, module,
                                               listener['ListenerArn'])

    # Get the ELB attributes again
    elb.update(get_elb_attributes(connection, module, elb['LoadBalancerArn']))

    # Convert to snake_case
    snaked_elb = camel_dict_to_snake_dict(elb)

    # Get the tags of the ELB
    elb_tags = connection.describe_tags(
        ResourceArns=[elb['LoadBalancerArn']])['TagDescriptions'][0]['Tags']
    snaked_elb['tags'] = boto3_tag_list_to_ansible_dict(elb_tags)

    module.exit_json(changed=changed, **snaked_elb)
Example #8
0
def create_launch_config(connection, module):
    name = module.params.get('name')
    vpc_id = module.params.get('vpc_id')
    try:
        region, ec2_url, aws_connect_kwargs = get_aws_connection_info(
            module, boto3=True)
        ec2_connection = boto3_conn(module, 'client', 'ec2', region, ec2_url,
                                    **aws_connect_kwargs)
        security_groups = get_ec2_security_group_ids_from_names(
            module.params.get('security_groups'),
            ec2_connection,
            vpc_id=vpc_id,
            boto3=True)
    except botocore.exceptions.ClientError as e:
        module.fail_json(msg="Failed to get Security Group IDs",
                         exception=traceback.format_exc(),
                         **camel_dict_to_snake_dict(e.response))
    except ValueError as e:
        module.fail_json(msg="Failed to get Security Group IDs",
                         exception=traceback.format_exc())
    user_data = module.params.get('user_data')
    user_data_path = module.params.get('user_data_path')
    volumes = module.params['volumes']
    instance_monitoring = module.params.get('instance_monitoring')
    assign_public_ip = module.params.get('assign_public_ip')
    instance_profile_name = module.params.get('instance_profile_name')
    ebs_optimized = module.params.get('ebs_optimized')
    classic_link_vpc_id = module.params.get('classic_link_vpc_id')
    classic_link_vpc_security_groups = module.params.get(
        'classic_link_vpc_security_groups')

    block_device_mapping = []

    convert_list = [
        'image_id', 'instance_type', 'instance_type', 'instance_id',
        'placement_tenancy', 'key_name', 'kernel_id', 'ramdisk_id',
        'spot_price'
    ]

    launch_config = (snake_dict_to_camel_dict(
        dict((k.capitalize(), str(v)) for k, v in module.params.items()
             if v is not None and k in convert_list)))

    if user_data_path:
        try:
            with open(user_data_path, 'r') as user_data_file:
                user_data = user_data_file.read()
        except IOError as e:
            module.fail_json(msg="Failed to open file for reading",
                             exception=traceback.format_exc())

    if volumes:
        for volume in volumes:
            if 'device_name' not in volume:
                module.fail_json(msg='Device name must be set for volume')
            # Minimum volume size is 1GiB. We'll use volume size explicitly set to 0 to be a signal not to create this volume
            if 'volume_size' not in volume or int(volume['volume_size']) > 0:
                block_device_mapping.append(
                    create_block_device_meta(module, volume))

    try:
        launch_configs = connection.describe_launch_configurations(
            LaunchConfigurationNames=[name]).get('LaunchConfigurations')
    except botocore.exceptions.ClientError as e:
        module.fail_json(msg="Failed to describe launch configuration by name",
                         exception=traceback.format_exc(),
                         **camel_dict_to_snake_dict(e.response))

    changed = False
    result = {}

    launch_config['LaunchConfigurationName'] = name

    if security_groups is not None:
        launch_config['SecurityGroups'] = security_groups

    if classic_link_vpc_id is not None:
        launch_config['ClassicLinkVPCId'] = classic_link_vpc_id

    if instance_monitoring is not None:
        launch_config['InstanceMonitoring'] = {'Enabled': instance_monitoring}

    if classic_link_vpc_security_groups is not None:
        launch_config[
            'ClassicLinkVPCSecurityGroups'] = classic_link_vpc_security_groups

    if block_device_mapping:
        launch_config['BlockDeviceMappings'] = block_device_mapping

    if instance_profile_name is not None:
        launch_config['IamInstanceProfile'] = instance_profile_name

    if assign_public_ip is not None:
        launch_config['AssociatePublicIpAddress'] = assign_public_ip

    if user_data is not None:
        launch_config['UserData'] = user_data

    if ebs_optimized is not None:
        launch_config['EbsOptimized'] = ebs_optimized

    if len(launch_configs) == 0:
        try:
            connection.create_launch_configuration(**launch_config)
            launch_configs = connection.describe_launch_configurations(
                LaunchConfigurationNames=[name]).get('LaunchConfigurations')
            changed = True
            if launch_configs:
                launch_config = launch_configs[0]
        except botocore.exceptions.ClientError as e:
            module.fail_json(msg="Failed to create launch configuration",
                             exception=traceback.format_exc(),
                             **camel_dict_to_snake_dict(e.response))

    result = (dict((k, v) for k, v in launch_config.items() if k not in [
        'Connection', 'CreatedTime', 'InstanceMonitoring',
        'BlockDeviceMappings'
    ]))

    result['CreatedTime'] = to_text(launch_config.get('CreatedTime'))

    try:
        result['InstanceMonitoring'] = module.boolean(
            launch_config.get('InstanceMonitoring').get('Enabled'))
    except AttributeError:
        result['InstanceMonitoring'] = False

    result['BlockDeviceMappings'] = []

    for block_device_mapping in launch_config.get('BlockDeviceMappings', []):
        result['BlockDeviceMappings'].append(
            dict(device_name=block_device_mapping.get('DeviceName'),
                 virtual_name=block_device_mapping.get('VirtualName')))
        if block_device_mapping.get('Ebs') is not None:
            result['BlockDeviceMappings'][-1]['ebs'] = dict(
                snapshot_id=block_device_mapping.get('Ebs').get('SnapshotId'),
                volume_size=block_device_mapping.get('Ebs').get('VolumeSize'))

    if user_data_path:
        result[
            'UserData'] = "hidden"  # Otherwise, we dump binary to the user's terminal

    return_object = {
        'Name': result.get('LaunchConfigurationName'),
        'CreatedTime': result.get('CreatedTime'),
        'ImageId': result.get('ImageId'),
        'Arn': result.get('LaunchConfigurationARN'),
        'SecurityGroups': result.get('SecurityGroups'),
        'InstanceType': result.get('InstanceType'),
        'Result': result
    }

    module.exit_json(changed=changed,
                     **camel_dict_to_snake_dict(return_object))
def modify_eni(connection, vpc_id, module, eni):

    instance_id = module.params.get("instance_id")
    attached = module.params.get("attached")
    do_detach = module.params.get('state') == 'detached'
    device_index = module.params.get("device_index")
    description = module.params.get('description')
    security_groups = module.params.get('security_groups')
    force_detach = module.params.get("force_detach")
    source_dest_check = module.params.get("source_dest_check")
    delete_on_termination = module.params.get("delete_on_termination")
    secondary_private_ip_addresses = module.params.get(
        "secondary_private_ip_addresses")
    purge_secondary_private_ip_addresses = module.params.get(
        "purge_secondary_private_ip_addresses")
    secondary_private_ip_address_count = module.params.get(
        "secondary_private_ip_address_count")
    allow_reassignment = module.params.get("allow_reassignment")
    changed = False

    try:
        if description is not None:
            if eni.description != description:
                connection.modify_network_interface_attribute(
                    eni.id, "description", description)
                changed = True
        if len(security_groups) > 0:
            groups = get_ec2_security_group_ids_from_names(security_groups,
                                                           connection,
                                                           vpc_id=vpc_id,
                                                           boto3=False)
            if sorted(get_sec_group_list(eni.groups)) != sorted(groups):
                connection.modify_network_interface_attribute(
                    eni.id, "groupSet", groups)
                changed = True
        if source_dest_check is not None:
            if eni.source_dest_check != source_dest_check:
                connection.modify_network_interface_attribute(
                    eni.id, "sourceDestCheck", source_dest_check)
                changed = True
        if delete_on_termination is not None and eni.attachment is not None:
            if eni.attachment.delete_on_termination is not delete_on_termination:
                connection.modify_network_interface_attribute(
                    eni.id, "deleteOnTermination", delete_on_termination,
                    eni.attachment.id)
                changed = True

        current_secondary_addresses = [
            i.private_ip_address for i in eni.private_ip_addresses
            if not i.primary
        ]
        if secondary_private_ip_addresses is not None:
            secondary_addresses_to_remove = list(
                set(current_secondary_addresses) -
                set(secondary_private_ip_addresses))
            if secondary_addresses_to_remove and purge_secondary_private_ip_addresses:
                connection.unassign_private_ip_addresses(
                    network_interface_id=eni.id,
                    private_ip_addresses=list(
                        set(current_secondary_addresses) -
                        set(secondary_private_ip_addresses)),
                    dry_run=False)
                changed = True

            secondary_addresses_to_add = list(
                set(secondary_private_ip_addresses) -
                set(current_secondary_addresses))
            if secondary_addresses_to_add:
                connection.assign_private_ip_addresses(
                    network_interface_id=eni.id,
                    private_ip_addresses=secondary_addresses_to_add,
                    secondary_private_ip_address_count=None,
                    allow_reassignment=allow_reassignment,
                    dry_run=False)
                changed = True
        if secondary_private_ip_address_count is not None:
            current_secondary_address_count = len(current_secondary_addresses)

            if secondary_private_ip_address_count > current_secondary_address_count:
                connection.assign_private_ip_addresses(
                    network_interface_id=eni.id,
                    private_ip_addresses=None,
                    secondary_private_ip_address_count=(
                        secondary_private_ip_address_count -
                        current_secondary_address_count),
                    allow_reassignment=allow_reassignment,
                    dry_run=False)
                changed = True
            elif secondary_private_ip_address_count < current_secondary_address_count:
                # How many of these addresses do we want to remove
                secondary_addresses_to_remove_count = current_secondary_address_count - secondary_private_ip_address_count
                connection.unassign_private_ip_addresses(
                    network_interface_id=eni.id,
                    private_ip_addresses=
                    current_secondary_addresses[:
                                                secondary_addresses_to_remove_count],
                    dry_run=False)

        if attached is True:
            if eni.attachment and eni.attachment.instance_id != instance_id:
                detach_eni(eni, module)
                eni.attach(instance_id, device_index)
                wait_for_eni(eni, "attached")
                changed = True
            if eni.attachment is None:
                eni.attach(instance_id, device_index)
                wait_for_eni(eni, "attached")
                changed = True
        elif attached is False:
            detach_eni(eni, module)

    except BotoServerError as e:
        module.fail_json(msg=e.message)

    eni.update()
    module.exit_json(changed=changed, interface=get_eni_info(eni))
Example #10
0
def modify_eni(connection, vpc_id, module, eni):

    instance_id = module.params.get("instance_id")
    attached = module.params.get("attached")
    do_detach = module.params.get('state') == 'detached'
    device_index = module.params.get("device_index")
    description = module.params.get('description')
    security_groups = module.params.get('security_groups')
    force_detach = module.params.get("force_detach")
    source_dest_check = module.params.get("source_dest_check")
    delete_on_termination = module.params.get("delete_on_termination")
    secondary_private_ip_addresses = module.params.get("secondary_private_ip_addresses")
    secondary_private_ip_address_count = module.params.get("secondary_private_ip_address_count")
    changed = False

    try:
        if description is not None:
            if eni.description != description:
                connection.modify_network_interface_attribute(eni.id, "description", description)
                changed = True
        if len(security_groups) > 0:
            groups = get_ec2_security_group_ids_from_names(security_groups, connection, vpc_id=vpc_id, boto3=False)
            if sorted(get_sec_group_list(eni.groups)) != sorted(groups):
                connection.modify_network_interface_attribute(eni.id, "groupSet", groups)
                changed = True
        if source_dest_check is not None:
            if eni.source_dest_check != source_dest_check:
                connection.modify_network_interface_attribute(eni.id, "sourceDestCheck", source_dest_check)
                changed = True
        if delete_on_termination is not None and eni.attachment is not None:
            if eni.attachment.delete_on_termination is not delete_on_termination:
                connection.modify_network_interface_attribute(eni.id, "deleteOnTermination", delete_on_termination, eni.attachment.id)
                changed = True

        current_secondary_addresses = [i.private_ip_address for i in eni.private_ip_addresses if not i.primary]
        if secondary_private_ip_addresses is not None:
            secondary_addresses_to_remove = list(set(current_secondary_addresses) - set(secondary_private_ip_addresses))
            if secondary_addresses_to_remove:
                connection.unassign_private_ip_addresses(network_interface_id=eni.id,
                                                         private_ip_addresses=list(set(current_secondary_addresses) -
                                                                                   set(secondary_private_ip_addresses)),
                                                         dry_run=False)
            connection.assign_private_ip_addresses(network_interface_id=eni.id,
                                                   private_ip_addresses=secondary_private_ip_addresses,
                                                   secondary_private_ip_address_count=None,
                                                   allow_reassignment=False, dry_run=False)
        if secondary_private_ip_address_count is not None:
            current_secondary_address_count = len(current_secondary_addresses)

            if secondary_private_ip_address_count > current_secondary_address_count:
                connection.assign_private_ip_addresses(network_interface_id=eni.id,
                                                       private_ip_addresses=None,
                                                       secondary_private_ip_address_count=(secondary_private_ip_address_count -
                                                                                           current_secondary_address_count),
                                                       allow_reassignment=False, dry_run=False)
                changed = True
            elif secondary_private_ip_address_count < current_secondary_address_count:
                # How many of these addresses do we want to remove
                secondary_addresses_to_remove_count = current_secondary_address_count - secondary_private_ip_address_count
                connection.unassign_private_ip_addresses(network_interface_id=eni.id,
                                                         private_ip_addresses=current_secondary_addresses[:secondary_addresses_to_remove_count],
                                                         dry_run=False)

        if attached is True:
            if eni.attachment and eni.attachment.instance_id != instance_id:
                detach_eni(eni, module)
                eni.attach(instance_id, device_index)
                wait_for_eni(eni, "attached")
                changed = True
            if eni.attachment is None:
                eni.attach(instance_id, device_index)
                wait_for_eni(eni, "attached")
                changed = True
        elif attached is False:
            detach_eni(eni, module)

    except BotoServerError as e:
        module.fail_json(msg=e.message)

    eni.update()
    module.exit_json(changed=changed, interface=get_eni_info(eni))
Example #11
0
def create_or_update_glue_connection(connection, connection_ec2, module,
                                     glue_connection):
    """
    Create or update an AWS Glue connection

    :param connection: AWS boto3 glue connection
    :param module: Ansible module
    :param glue_connection: a dict of AWS Glue connection parameters or None
    :return:
    """

    changed = False
    params = dict()
    params['ConnectionInput'] = dict()
    params['ConnectionInput']['Name'] = module.params.get("name")
    params['ConnectionInput']['ConnectionType'] = module.params.get(
        "connection_type")
    params['ConnectionInput']['ConnectionProperties'] = module.params.get(
        "connection_properties")
    if module.params.get("catalog_id") is not None:
        params['CatalogId'] = module.params.get("catalog_id")
    if module.params.get("description") is not None:
        params['ConnectionInput']['Description'] = module.params.get(
            "description")
    if module.params.get("match_criteria") is not None:
        params['ConnectionInput']['MatchCriteria'] = module.params.get(
            "match_criteria")
    if module.params.get("security_groups") is not None or module.params.get(
            "subnet_id") is not None:
        params['ConnectionInput']['PhysicalConnectionRequirements'] = dict()
    if module.params.get("security_groups") is not None:
        # Get security group IDs from names
        security_group_ids = get_ec2_security_group_ids_from_names(
            module.params.get('security_groups'), connection_ec2, boto3=True)
        params['ConnectionInput']['PhysicalConnectionRequirements'][
            'SecurityGroupIdList'] = security_group_ids
    if module.params.get("subnet_id") is not None:
        params['ConnectionInput']['PhysicalConnectionRequirements'][
            'SubnetId'] = module.params.get("subnet_id")

    # If glue_connection is not None then check if it needs to be modified, else create it
    if glue_connection:
        if _compare_glue_connection_params(params, glue_connection):
            try:
                # We need to slightly modify the params for an update
                update_params = copy.deepcopy(params)
                update_params['Name'] = update_params['ConnectionInput'][
                    'Name']
                connection.update_connection(**update_params)
                changed = True
            except (BotoCoreError, ClientError) as e:
                module.fail_json_aws(e)
    else:
        try:
            connection.create_connection(**params)
            changed = True
        except (BotoCoreError, ClientError) as e:
            module.fail_json_aws(e)

    # If changed, get the Glue connection again
    if changed:
        glue_connection = None
        for i in range(10):
            glue_connection = _get_glue_connection(connection, module)
            if glue_connection is not None:
                break
            time.sleep(10)

    module.exit_json(changed=changed,
                     **camel_dict_to_snake_dict(glue_connection))
Example #12
0
def create_or_update_elb(connection, connection_ec2, module):
    """Create ELB or modify main attributes. json_exit here"""

    changed = False
    new_load_balancer = False
    params = dict()
    params['Name'] = module.params.get("name")
    params['Subnets'] = module.params.get("subnets")
    try:
        params['SecurityGroups'] = get_ec2_security_group_ids_from_names(
            module.params.get('security_groups'), connection_ec2, boto3=True)
    except ValueError as e:
        module.fail_json(msg=str(e))
    params['Scheme'] = module.params.get("scheme")
    if module.params.get("tags"):
        params['Tags'] = ansible_dict_to_boto3_tag_list(
            module.params.get("tags"))
    purge_tags = module.params.get("purge_tags")

    # Does the ELB currently exist?
    elb = get_elb(connection, module)

    if elb:
        # ELB exists so check subnets, security groups and tags match what has been passed

        # Subnets
        if set(_get_subnet_ids_from_subnet_list(
                elb['AvailabilityZones'])) != set(params['Subnets']):
            try:
                connection.set_subnets(LoadBalancerArn=elb['LoadBalancerArn'],
                                       Subnets=params['Subnets'])
            except (ClientError, NoCredentialsError) as e:
                module.fail_json(msg=e.message,
                                 exception=traceback.format_exc(),
                                 **camel_dict_to_snake_dict(e.response))
            changed = True

        # Security Groups
        if set(elb['SecurityGroups']) != set(params['SecurityGroups']):
            try:
                connection.set_security_groups(
                    LoadBalancerArn=elb['LoadBalancerArn'],
                    SecurityGroups=params['SecurityGroups'])
            except (ClientError, NoCredentialsError) as e:
                module.fail_json(msg=e.message,
                                 exception=traceback.format_exc(),
                                 **camel_dict_to_snake_dict(e.response))
            changed = True

        # Tags - only need to play with tags if tags parameter has been set to something
        if module.params.get("tags"):
            try:
                elb_tags = connection.describe_tags(
                    ResourceArns=[elb['LoadBalancerArn']
                                  ])['TagDescriptions'][0]['Tags']
            except (ClientError, NoCredentialsError) as e:
                module.fail_json(msg=e.message,
                                 exception=traceback.format_exc(),
                                 **camel_dict_to_snake_dict(e.response))

            # Delete necessary tags
            tags_to_delete, tags_need_modify = _compare_tags(
                elb_tags, params['Tags'], purge_tags)
            if tags_to_delete:
                try:
                    connection.remove_tags(
                        ResourceArns=[elb['LoadBalancerArn']],
                        TagKeys=tags_to_delete)
                except (ClientError, NoCredentialsError) as e:
                    module.fail_json(msg=e.message,
                                     **camel_dict_to_snake_dict(e.response))
                changed = True

            # Add/update tags
            if tags_need_modify:
                try:
                    connection.add_tags(ResourceArns=[elb['LoadBalancerArn']],
                                        Tags=params['Tags'])
                except (ClientError, NoCredentialsError) as e:
                    module.fail_json(msg=e.message,
                                     exception=traceback.format_exc(),
                                     **camel_dict_to_snake_dict(e.response))
                changed = True

    else:
        try:
            elb = connection.create_load_balancer(**params)['LoadBalancers'][0]
            changed = True
            new_load_balancer = True
        except (ClientError, NoCredentialsError) as e:
            module.fail_json(msg=e.message,
                             exception=traceback.format_exc(),
                             **camel_dict_to_snake_dict(e.response))

        if module.params.get("wait"):
            status_achieved, new_elb = wait_for_status(connection, module,
                                                       elb['LoadBalancerArn'],
                                                       'active')

    # Now set ELB attributes. Use try statement here so we can remove the ELB if this stage fails
    try:
        attribute_changed = update_elb_attributes(connection, module, elb)
        if attribute_changed:
            changed = True
    except (ClientError, NoCredentialsError) as e:
        # Something went wrong setting attributes. If this ELB was created during this task, delete it to leave a consistent state
        if new_load_balancer:
            connection.delete_load_balancer(
                LoadBalancerArn=elb['LoadBalancerArn'])
        module.fail_json(msg=e.message,
                         exception=traceback.format_exc(),
                         **camel_dict_to_snake_dict(e.response))

    # Now, if required, set ELB listeners. Use try statement here so we can remove the ELB if this stage fails
    try:
        listener_changed = create_or_update_elb_listeners(
            connection, module, elb)
        if listener_changed:
            changed = True
    except (ClientError, NoCredentialsError) as e:
        # Something went wrong setting listeners. If this ELB was created during this task, delete it to leave a consistent state
        if new_load_balancer:
            connection.delete_load_balancer(
                LoadBalancerArn=elb['LoadBalancerArn'])
        module.fail_json(msg=e.message,
                         exception=traceback.format_exc(),
                         **camel_dict_to_snake_dict(e.response))

    # Get the ELB again
    elb = get_elb(connection, module)
    # Get the tags of the ELB
    elb_tags = connection.describe_tags(
        ResourceArns=[elb['LoadBalancerArn']])['TagDescriptions'][0]['Tags']
    elb['Tags'] = boto3_tag_list_to_ansible_dict(elb_tags)

    module.exit_json(changed=changed,
                     load_balancer=camel_dict_to_snake_dict(elb))
Example #13
0
def create_launch_config(connection, module):
    name = module.params.get('name')
    vpc_id = module.params.get('vpc_id')
    try:
        region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module, boto3=True)
        ec2_connection = boto3_conn(module, 'client', 'ec2', region, ec2_url, **aws_connect_kwargs)
        security_groups = get_ec2_security_group_ids_from_names(module.params.get('security_groups'), ec2_connection, vpc_id=vpc_id, boto3=True)
    except botocore.exceptions.ClientError as e:
        module.fail_json(msg="Failed to get Security Group IDs", exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))
    except ValueError as e:
        module.fail_json(msg="Failed to get Security Group IDs", exception=traceback.format_exc())
    user_data = module.params.get('user_data')
    user_data_path = module.params.get('user_data_path')
    volumes = module.params['volumes']
    instance_monitoring = module.params.get('instance_monitoring')
    assign_public_ip = module.params.get('assign_public_ip')
    instance_profile_name = module.params.get('instance_profile_name')
    ebs_optimized = module.params.get('ebs_optimized')
    classic_link_vpc_id = module.params.get('classic_link_vpc_id')
    classic_link_vpc_security_groups = module.params.get('classic_link_vpc_security_groups')

    block_device_mapping = {}

    convert_list = ['image_id', 'instance_type', 'instance_type', 'instance_id', 'placement_tenancy', 'key_name', 'kernel_id', 'ramdisk_id', 'spot_price']

    launch_config = (snake_dict_to_camel_dict(dict((k.capitalize(), str(v)) for k, v in module.params.items() if v is not None and k in convert_list)))

    if user_data_path:
        try:
            with open(user_data_path, 'r') as user_data_file:
                user_data = user_data_file.read()
        except IOError as e:
            module.fail_json(msg="Failed to open file for reading", exception=traceback.format_exc())

    if volumes:
        for volume in volumes:
            if 'device_name' not in volume:
                module.fail_json(msg='Device name must be set for volume')
            # Minimum volume size is 1GB. We'll use volume size explicitly set to 0 to be a signal not to create this volume
            if 'volume_size' not in volume or int(volume['volume_size']) > 0:
                block_device_mapping.update(create_block_device_meta(module, volume))

    try:
        launch_configs = connection.describe_launch_configurations(LaunchConfigurationNames=[name]).get('LaunchConfigurations')
    except botocore.exceptions.ClientError as e:
        module.fail_json(msg="Failed to describe launch configuration by name", exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))

    changed = False
    result = {}

    launch_config['LaunchConfigurationName'] = name

    if security_groups is not None:
        launch_config['SecurityGroups'] = security_groups

    if classic_link_vpc_id is not None:
        launch_config['ClassicLinkVPCId'] = classic_link_vpc_id

    if instance_monitoring:
        launch_config['InstanceMonitoring'] = {'Enabled': instance_monitoring}

    if classic_link_vpc_security_groups is not None:
        launch_config['ClassicLinkVPCSecurityGroups'] = classic_link_vpc_security_groups

    if block_device_mapping:
        launch_config['BlockDeviceMappings'] = [block_device_mapping]

    if instance_profile_name is not None:
        launch_config['IamInstanceProfile'] = instance_profile_name

    if assign_public_ip is not None:
        launch_config['AssociatePublicIpAddress'] = assign_public_ip

    if user_data is not None:
        launch_config['UserData'] = user_data

    if ebs_optimized is not None:
        launch_config['EbsOptimized'] = ebs_optimized

    if len(launch_configs) == 0:
        try:
            connection.create_launch_configuration(**launch_config)
            launch_configs = connection.describe_launch_configurations(LaunchConfigurationNames=[name]).get('LaunchConfigurations')
            changed = True
            if launch_configs:
                launch_config = launch_configs[0]
        except botocore.exceptions.ClientError as e:
            module.fail_json(msg="Failed to create launch configuration", exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))

    result = (dict((k, v) for k, v in launch_config.items()
              if k not in ['Connection', 'CreatedTime', 'InstanceMonitoring', 'BlockDeviceMappings']))

    result['CreatedTime'] = to_text(launch_config.get('CreatedTime'))

    try:
        result['InstanceMonitoring'] = module.boolean(launch_config.get('InstanceMonitoring').get('Enabled'))
    except AttributeError:
        result['InstanceMonitoring'] = False

    result['BlockDeviceMappings'] = []

    for block_device_mapping in launch_config.get('BlockDeviceMappings', []):
        result['BlockDeviceMappings'].append(dict(device_name=block_device_mapping.get('DeviceName'), virtual_name=block_device_mapping.get('VirtualName')))
        if block_device_mapping.get('Ebs') is not None:
            result['BlockDeviceMappings'][-1]['ebs'] = dict(
                snapshot_id=block_device_mapping.get('Ebs').get('SnapshotId'), volume_size=block_device_mapping.get('Ebs').get('VolumeSize'))

    if user_data_path:
        result['UserData'] = "hidden"  # Otherwise, we dump binary to the user's terminal

    return_object = {
        'Name': result.get('LaunchConfigurationName'),
        'CreatedTime': result.get('CreatedTime'),
        'ImageId': result.get('ImageId'),
        'Arn': result.get('LaunchConfigurationARN'),
        'SecurityGroups': result.get('SecurityGroups'),
        'InstanceType': result.get('InstanceType'),
        'Result': result
    }

    module.exit_json(changed=changed, **camel_dict_to_snake_dict(return_object))
Example #14
0
def create_launch_config(connection, module):
    name = module.params.get('name')
    image_id = module.params.get('image_id')
    key_name = module.params.get('key_name')
    try:
        security_groups = get_ec2_security_group_ids_from_names(
            module.params.get('security_groups'),
            ec2_connect(module),
            vpc_id=None,
            boto3=False)
    except ValueError as e:
        module.fail_json(msg=str(e))
    user_data = module.params.get('user_data')
    user_data_path = module.params.get('user_data_path')
    volumes = module.params['volumes']
    instance_type = module.params.get('instance_type')
    spot_price = module.params.get('spot_price')
    instance_monitoring = module.params.get('instance_monitoring')
    assign_public_ip = module.params.get('assign_public_ip')
    kernel_id = module.params.get('kernel_id')
    ramdisk_id = module.params.get('ramdisk_id')
    instance_profile_name = module.params.get('instance_profile_name')
    ebs_optimized = module.params.get('ebs_optimized')
    classic_link_vpc_id = module.params.get('classic_link_vpc_id')
    classic_link_vpc_security_groups = module.params.get(
        'classic_link_vpc_security_groups')
    bdm = BlockDeviceMapping()

    if user_data_path:
        try:
            with open(user_data_path, 'r') as user_data_file:
                user_data = user_data_file.read()
        except IOError as e:
            module.fail_json(msg=str(e), exception=traceback.format_exc())

    if volumes:
        for volume in volumes:
            if 'device_name' not in volume:
                module.fail_json(msg='Device name must be set for volume')
            # Minimum volume size is 1GB. We'll use volume size explicitly set to 0
            # to be a signal not to create this volume
            if 'volume_size' not in volume or int(volume['volume_size']) > 0:
                bdm[volume['device_name']] = create_block_device(
                    module, volume)

    lc = LaunchConfiguration(
        name=name,
        image_id=image_id,
        key_name=key_name,
        security_groups=security_groups,
        user_data=user_data,
        block_device_mappings=[bdm],
        instance_type=instance_type,
        kernel_id=kernel_id,
        spot_price=spot_price,
        instance_monitoring=instance_monitoring,
        associate_public_ip_address=assign_public_ip,
        ramdisk_id=ramdisk_id,
        instance_profile_name=instance_profile_name,
        ebs_optimized=ebs_optimized,
        classic_link_vpc_security_groups=classic_link_vpc_security_groups,
        classic_link_vpc_id=classic_link_vpc_id,
    )

    launch_configs = connection.get_all_launch_configurations(names=[name])
    changed = False
    if not launch_configs:
        try:
            connection.create_launch_configuration(lc)
            launch_configs = connection.get_all_launch_configurations(
                names=[name])
            changed = True
        except BotoServerError as e:
            module.fail_json(msg=str(e))

    result = dict(
        ((a[0], a[1]) for a in vars(launch_configs[0]).items()
         if a[0] not in ('connection', 'created_time', 'instance_monitoring',
                         'block_device_mappings')))
    result['created_time'] = str(launch_configs[0].created_time)
    # Looking at boto's launchconfig.py, it looks like this could be a boolean
    # value or an object with an enabled attribute.  The enabled attribute
    # could be a boolean or a string representation of a boolean.  Since
    # I can't test all permutations myself to see if my reading of the code is
    # correct, have to code this *very* defensively
    if launch_configs[0].instance_monitoring is True:
        result['instance_monitoring'] = True
    else:
        try:
            result['instance_monitoring'] = module.boolean(
                launch_configs[0].instance_monitoring.enabled)
        except AttributeError:
            result['instance_monitoring'] = False
    if launch_configs[0].block_device_mappings is not None:
        result['block_device_mappings'] = []
        for bdm in launch_configs[0].block_device_mappings:
            result['block_device_mappings'].append(
                dict(device_name=bdm.device_name,
                     virtual_name=bdm.virtual_name))
            if bdm.ebs is not None:
                result['block_device_mappings'][-1]['ebs'] = dict(
                    snapshot_id=bdm.ebs.snapshot_id,
                    volume_size=bdm.ebs.volume_size)

    if user_data_path:
        result[
            'user_data'] = "hidden"  # Otherwise, we dump binary to the user's terminal

    module.exit_json(changed=changed,
                     name=result['name'],
                     created_time=result['created_time'],
                     image_id=result['image_id'],
                     arn=result['launch_configuration_arn'],
                     security_groups=result['security_groups'],
                     instance_type=result['instance_type'],
                     result=result)
Example #15
0
def create_or_update_elb(connection, connection_ec2, module):
    """Create ELB or modify main attributes. json_exit here"""

    changed = False
    new_load_balancer = False
    params = dict()
    params['Name'] = module.params.get("name")
    params['Subnets'] = module.params.get("subnets")
    try:
        params['SecurityGroups'] = get_ec2_security_group_ids_from_names(module.params.get('security_groups'), connection_ec2, boto3=True)
    except ValueError as e:
        module.fail_json(msg=str(e), exception=traceback.format_exc())
    except ClientError as e:
        module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))
    except NoCredentialsError as e:
        module.fail_json(msg="AWS authentication problem. " + e.message, exception=traceback.format_exc())

    params['Scheme'] = module.params.get("scheme")
    if module.params.get("tags"):
        params['Tags'] = ansible_dict_to_boto3_tag_list(module.params.get("tags"))
    purge_tags = module.params.get("purge_tags")
    access_logs_enabled = module.params.get("access_logs_enabled")
    access_logs_s3_bucket = module.params.get("access_logs_s3_bucket")
    access_logs_s3_prefix = module.params.get("access_logs_s3_prefix")
    deletion_protection = module.params.get("deletion_protection")
    idle_timeout = module.params.get("idle_timeout")

    # Does the ELB currently exist?
    elb = get_elb(connection, module)

    if elb:
        # ELB exists so check subnets, security groups and tags match what has been passed

        # Subnets
        if set(_get_subnet_ids_from_subnet_list(elb['AvailabilityZones'])) != set(params['Subnets']):
            try:
                connection.set_subnets(LoadBalancerArn=elb['LoadBalancerArn'], Subnets=params['Subnets'])
            except ClientError as e:
                module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))
            changed = True

        # Security Groups
        if set(elb['SecurityGroups']) != set(params['SecurityGroups']):
            try:
                connection.set_security_groups(LoadBalancerArn=elb['LoadBalancerArn'], SecurityGroups=params['SecurityGroups'])
            except ClientError as e:
                module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))
            changed = True

        # Tags - only need to play with tags if tags parameter has been set to something
        if module.params.get("tags"):
            try:
                elb_tags = connection.describe_tags(ResourceArns=[elb['LoadBalancerArn']])['TagDescriptions'][0]['Tags']
            except ClientError as e:
                module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))

            # Delete necessary tags
            tags_need_modify, tags_to_delete = compare_aws_tags(boto3_tag_list_to_ansible_dict(elb_tags), boto3_tag_list_to_ansible_dict(params['Tags']),
                                                                purge_tags)
            if tags_to_delete:
                try:
                    connection.remove_tags(ResourceArns=[elb['LoadBalancerArn']], TagKeys=tags_to_delete)
                except ClientError as e:
                    module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))
                changed = True

            # Add/update tags
            if tags_need_modify:
                try:
                    connection.add_tags(ResourceArns=[elb['LoadBalancerArn']], Tags=params['Tags'])
                except ClientError as e:
                    module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))
                changed = True

    else:
        try:
            elb = connection.create_load_balancer(**params)['LoadBalancers'][0]
            changed = True
            new_load_balancer = True
        except ClientError as e:
            module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))

        if module.params.get("wait"):
            status_achieved, new_elb = wait_for_status(connection, module, elb['LoadBalancerArn'], 'active')

    # Now set ELB attributes. Use try statement here so we can remove the ELB if this stage fails
    update_attributes = []

    # Get current attributes
    current_elb_attributes = get_elb_attributes(connection, module, elb['LoadBalancerArn'])

    if access_logs_enabled and current_elb_attributes['access_logs_s3_enabled'] != "true":
        update_attributes.append({'Key': 'access_logs.s3.enabled', 'Value': "true"})
    if not access_logs_enabled and current_elb_attributes['access_logs_s3_enabled'] != "false":
        update_attributes.append({'Key': 'access_logs.s3.enabled', 'Value': 'false'})
    if access_logs_s3_bucket is not None and access_logs_s3_bucket != current_elb_attributes['access_logs_s3_bucket']:
        update_attributes.append({'Key': 'access_logs.s3.bucket', 'Value': access_logs_s3_bucket})
    if access_logs_s3_prefix is not None and access_logs_s3_prefix != current_elb_attributes['access_logs_s3_prefix']:
        update_attributes.append({'Key': 'access_logs.s3.prefix', 'Value': access_logs_s3_prefix})
    if deletion_protection and current_elb_attributes['deletion_protection_enabled'] != "true":
        update_attributes.append({'Key': 'deletion_protection.enabled', 'Value': "true"})
    if not deletion_protection and current_elb_attributes['deletion_protection_enabled'] != "false":
        update_attributes.append({'Key': 'deletion_protection.enabled', 'Value': "false"})
    if idle_timeout is not None and str(idle_timeout) != current_elb_attributes['idle_timeout_timeout_seconds']:
        update_attributes.append({'Key': 'idle_timeout.timeout_seconds', 'Value': str(idle_timeout)})

    if update_attributes:
        try:
            connection.modify_load_balancer_attributes(LoadBalancerArn=elb['LoadBalancerArn'], Attributes=update_attributes)
            changed = True
        except ClientError as e:
            # Something went wrong setting attributes. If this ELB was created during this task, delete it to leave a consistent state
            if new_load_balancer:
                connection.delete_load_balancer(LoadBalancerArn=elb['LoadBalancerArn'])
            module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))

    # Now, if required, set ELB listeners. Use try statement here so we can remove the ELB if this stage fails
    try:
        listener_changed = create_or_update_elb_listeners(connection, module, elb)
        if listener_changed:
            changed = True
    except ClientError as e:
        # Something went wrong setting listeners. If this ELB was created during this task, delete it to leave a consistent state
        if new_load_balancer:
            connection.delete_load_balancer(LoadBalancerArn=elb['LoadBalancerArn'])
        module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))

    # Get the ELB again
    elb = get_elb(connection, module)

    # Get the ELB listeners again
    elb['listeners'] = get_elb_listeners(connection, module, elb['LoadBalancerArn'])

    # For each listener, get listener rules
    for listener in elb['listeners']:
        listener['rules'] = get_listener_rules(connection, module, listener['ListenerArn'])

    # Get the ELB attributes again
    elb.update(get_elb_attributes(connection, module, elb['LoadBalancerArn']))

    # Convert to snake_case
    snaked_elb = camel_dict_to_snake_dict(elb)

    # Get the tags of the ELB
    elb_tags = connection.describe_tags(ResourceArns=[elb['LoadBalancerArn']])['TagDescriptions'][0]['Tags']
    snaked_elb['tags'] = boto3_tag_list_to_ansible_dict(elb_tags)

    module.exit_json(changed=changed, **snaked_elb)