Exemplo n.º 1
0
    def modify_elb_attributes(self):
        """
        Update ELB attributes if required
        :return:
        """

        update_attributes = []

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

        if update_attributes:
            try:
                AWSRetry.jittered_backoff()(
                    self.connection.modify_load_balancer_attributes
                )(LoadBalancerArn=self.elb['LoadBalancerArn'], Attributes=update_attributes)
                self.changed = True
            except (BotoCoreError, ClientError) as e:
                # Something went wrong setting attributes. If this ELB was created during this task, delete it to leave a consistent state
                if self.new_load_balancer:
                    AWSRetry.jittered_backoff()(self.connection.delete_load_balancer)(LoadBalancerArn=self.elb['LoadBalancerArn'])
                self.module.fail_json_aws(e)
Exemplo n.º 2
0
    def modify(self):

        try:
            # Rules is not a valid parameter for modify_listener
            if 'Rules' in self.listener:
                self.listener.pop('Rules')
            AWSRetry.jittered_backoff()(self.connection.modify_listener)(**self.listener)
        except (BotoCoreError, ClientError) as e:
            self.module.fail_json_aws(e)
Exemplo n.º 3
0
    def add(self):

        try:
            # Rules is not a valid parameter for create_listener
            if 'Rules' in self.listener:
                self.listener.pop('Rules')
            AWSRetry.jittered_backoff()(self.connection.create_listener)(LoadBalancerArn=self.elb_arn, **self.listener)
        except (BotoCoreError, ClientError) as e:
            self.module.fail_json_aws(e)
Exemplo n.º 4
0
    def delete(self):
        """
        Delete a listener rule

        :return:
        """

        try:
            AWSRetry.jittered_backoff()(self.connection.delete_rule)(RuleArn=self.rule['RuleArn'])
        except (BotoCoreError, ClientError) as e:
            self.module.fail_json_aws(e)

        self.changed = True
Exemplo n.º 5
0
    def modify_security_groups(self):
        """
        Modify elb security groups to match module parameters
        :return:
        """

        try:
            AWSRetry.jittered_backoff()(
                self.connection.set_security_groups
            )(LoadBalancerArn=self.elb['LoadBalancerArn'], SecurityGroups=self.security_groups)
        except (BotoCoreError, ClientError) as e:
            self.module.fail_json_aws(e)

        self.changed = True
Exemplo n.º 6
0
    def delete(self):
        """
        Delete elb
        :return:
        """

        try:
            AWSRetry.jittered_backoff()(
                self.connection.delete_load_balancer
            )(LoadBalancerArn=self.elb['LoadBalancerArn'])
        except (BotoCoreError, ClientError) as e:
            self.module.fail_json_aws(e)

        self.changed = True
Exemplo n.º 7
0
    def modify(self):
        """
        Modify a listener rule

        :return:
        """

        try:
            del self.rule['Priority']
            AWSRetry.jittered_backoff()(self.connection.modify_rule)(**self.rule)
        except (BotoCoreError, ClientError) as e:
            self.module.fail_json_aws(e)

        self.changed = True
Exemplo n.º 8
0
    def modify_tags(self):
        """
        Modify elb tags

        :return:
        """

        try:
            AWSRetry.jittered_backoff()(
                self.connection.add_tags
            )(ResourceArns=[self.elb['LoadBalancerArn']], Tags=self.tags)
        except (BotoCoreError, ClientError) as e:
            self.module.fail_json_aws(e)

        self.changed = True
Exemplo n.º 9
0
    def delete_tags(self, tags_to_delete):
        """
        Delete elb tags

        :return:
        """

        try:
            AWSRetry.jittered_backoff()(
                self.connection.remove_tags
            )(ResourceArns=[self.elb['LoadBalancerArn']], TagKeys=tags_to_delete)
        except (BotoCoreError, ClientError) as e:
            self.module.fail_json_aws(e)

        self.changed = True
Exemplo n.º 10
0
    def create(self):
        """
        Create a listener rule

        :return:
        """

        try:
            self.rule['ListenerArn'] = self.listener_arn
            self.rule['Priority'] = int(self.rule['Priority'])
            AWSRetry.jittered_backoff()(self.connection.create_rule)(**self.rule)
        except (BotoCoreError, ClientError) as e:
            self.module.fail_json_aws(e)

        self.changed = True
Exemplo n.º 11
0
def update_vpc_tags(connection, module, vpc_id, tags, name):

    if tags is None:
        tags = dict()

    tags.update({'Name': name})
    try:
        current_tags = dict((t['Key'], t['Value']) for t in connection.describe_tags(Filters=[{'Name': 'resource-id', 'Values': [vpc_id]}])['Tags'])
        if tags != current_tags:
            if not module.check_mode:
                tags = ansible_dict_to_boto3_tag_list(tags)
                vpc_obj = AWSRetry.backoff(
                    delay=1, tries=5,
                    catch_extra_error_codes=['InvalidVpcID.NotFound'],
                )(connection.create_tags)(Resources=[vpc_id], Tags=tags)

                # Wait for tags to be updated
                expected_tags = boto3_tag_list_to_ansible_dict(tags)
                filters = [{'Name': 'tag:{0}'.format(key), 'Values': [value]} for key, value in expected_tags.items()]
                connection.get_waiter('vpc_available').wait(VpcIds=[vpc_id], Filters=filters)

            return True
        else:
            return False
    except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
        module.fail_json_aws(e, msg="Failed to update tags")
Exemplo n.º 12
0
    def __init__(self, connection, connection_ec2, module):
        """

        :param connection: boto3 connection
        :param module: Ansible module
        """
        super(ApplicationLoadBalancer, self).__init__(connection, module)

        self.connection_ec2 = connection_ec2

        # Ansible module parameters specific to ALBs
        self.type = 'application'
        if module.params.get('security_groups') is not None:
            try:
                self.security_groups = AWSRetry.jittered_backoff()(
                    get_ec2_security_group_ids_from_names
                )(module.params.get('security_groups'), self.connection_ec2, boto3=True)
            except ValueError as e:
                self.module.fail_json(msg=str(e), exception=traceback.format_exc())
            except (BotoCoreError, ClientError) as e:
                self.module.fail_json_aws(e)
        else:
            self.security_groups = module.params.get('security_groups')
        self.access_logs_enabled = module.params.get("access_logs_enabled")
        self.access_logs_s3_bucket = module.params.get("access_logs_s3_bucket")
        self.access_logs_s3_prefix = module.params.get("access_logs_s3_prefix")
        self.idle_timeout = module.params.get("idle_timeout")
Exemplo n.º 13
0
def get_elb_listener(connection, module, elb_arn, listener_port):
    """
    Get an ELB listener based on the port provided. If not found, return None.

    :param connection: AWS boto3 elbv2 connection
    :param module: Ansible module
    :param elb_arn: ARN of the ELB to look at
    :param listener_port: Port of the listener to look for
    :return: boto3 ELB listener dict or None if not found
    """

    try:
        listener_paginator = connection.get_paginator('describe_listeners')
        listeners = (AWSRetry.jittered_backoff()(listener_paginator.paginate)(LoadBalancerArn=elb_arn).build_full_result())['Listeners']
    except (BotoCoreError, ClientError) as e:
        module.fail_json_aws(e)

    l = None

    for listener in listeners:
        if listener['Port'] == listener_port:
            l = listener
            break

    return l
Exemplo n.º 14
0
    def create_elb(self):
        """
        Create a load balancer
        :return:
        """

        # Required parameters
        params = dict()
        params['Name'] = self.name
        params['Type'] = self.type

        # Other parameters
        if self.subnets is not None:
            params['Subnets'] = self.subnets
        params['Scheme'] = self.scheme
        if self.tags is not None:
            params['Tags'] = self.tags

        try:
            self.elb = AWSRetry.jittered_backoff()(self.connection.create_load_balancer)(**params)['LoadBalancers'][0]
            self.changed = True
            self.new_load_balancer = True
        except (BotoCoreError, ClientError) as e:
            self.module.fail_json_aws(e)

        if self.wait:
            self.wait_for_status(self.elb['LoadBalancerArn'])
Exemplo n.º 15
0
def ensure_tags(conn, module, subnet, tags, purge_tags, start_time):
    changed = False

    filters = ansible_dict_to_boto3_filter_list({'resource-id': subnet['id'], 'resource-type': 'subnet'})
    try:
        cur_tags = conn.describe_tags(Filters=filters)
    except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
        module.fail_json_aws(e, msg="Couldn't describe tags")

    to_update, to_delete = compare_aws_tags(boto3_tag_list_to_ansible_dict(cur_tags.get('Tags')), tags, purge_tags)

    if to_update:
        try:
            if not module.check_mode:
                AWSRetry.exponential_backoff(
                    catch_extra_error_codes=['InvalidSubnetID.NotFound']
                )(conn.create_tags)(
                    Resources=[subnet['id']],
                    Tags=ansible_dict_to_boto3_tag_list(to_update)
                )

            changed = True
        except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
            module.fail_json_aws(e, msg="Couldn't create tags")

    if to_delete:
        try:
            if not module.check_mode:
                tags_list = []
                for key in to_delete:
                    tags_list.append({'Key': key})

                AWSRetry.exponential_backoff(
                    catch_extra_error_codes=['InvalidSubnetID.NotFound']
                )(conn.delete_tags)(Resources=[subnet['id']], Tags=tags_list)

            changed = True
        except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
            module.fail_json_aws(e, msg="Couldn't delete tags")

    if module.params['wait'] and not module.check_mode:
        # Wait for tags to be updated
        filters = [{'Name': 'tag:{0}'.format(k), 'Values': [v]} for k, v in tags.items()]
        handle_waiter(conn, module, 'subnet_exists',
                      {'SubnetIds': [subnet['id']], 'Filters': filters}, start_time)

    return changed
Exemplo n.º 16
0
    def _get_elb_listeners(self):
        """
        Get ELB listeners

        :return:
        """

        try:
            listener_paginator = self.connection.get_paginator('describe_listeners')
            return (AWSRetry.jittered_backoff()(listener_paginator.paginate)(LoadBalancerArn=self.elb_arn).build_full_result())['Listeners']
        except (BotoCoreError, ClientError) as e:
            self.module.fail_json_aws(e)
Exemplo n.º 17
0
    def get_elb_tags(self):
        """
        Get load balancer tags

        :return:
        """

        try:
            return AWSRetry.jittered_backoff()(
                self.connection.describe_tags
            )(ResourceArns=[self.elb['LoadBalancerArn']])['TagDescriptions'][0]['Tags']
        except (BotoCoreError, ClientError) as e:
            self.module.fail_json_aws(e)
Exemplo n.º 18
0
def get_elb_listener_rules(connection, module, listener_arn):
    """
    Get rules for a particular ELB listener using the listener ARN.

    :param connection: AWS boto3 elbv2 connection
    :param module: Ansible module
    :param listener_arn: ARN of the ELB listener
    :return: boto3 ELB rules list
    """

    try:
        return AWSRetry.jittered_backoff()(connection.describe_rules)(ListenerArn=listener_arn)['Rules']
    except (BotoCoreError, ClientError) as e:
        module.fail_json_aws(e)
Exemplo n.º 19
0
    def modify_elb_attributes(self):
        """
        Update ELB attributes if required
        :return:
        """

        update_attributes = []

        if self.deletion_protection and self.elb_attributes['deletion_protection_enabled'] != "true":
            update_attributes.append({'Key': 'deletion_protection.enabled', 'Value': "true"})
        if self.deletion_protection is not None and not self.deletion_protection and self.elb_attributes['deletion_protection_enabled'] != "false":
            update_attributes.append({'Key': 'deletion_protection.enabled', 'Value': "false"})

        if update_attributes:
            try:
                AWSRetry.jittered_backoff()(
                    self.connection.modify_load_balancer_attributes
                )(LoadBalancerArn=self.elb['LoadBalancerArn'], Attributes=update_attributes)
                self.changed = True
            except (BotoCoreError, ClientError) as e:
                # Something went wrong setting attributes. If this ELB was created during this task, delete it to leave a consistent state
                if self.new_load_balancer:
                    AWSRetry.jittered_backoff()(self.connection.delete_load_balancer)(LoadBalancerArn=self.elb['LoadBalancerArn'])
                self.module.fail_json_aws(e)
Exemplo n.º 20
0
def attach_vgw(client, module, vpn_gateway_id):
    params = dict()
    params['VpcId'] = module.params.get('vpc_id')

    try:
        response = AWSRetry.jittered_backoff()(client.attach_vpn_gateway)(VpnGatewayId=vpn_gateway_id, VpcId=params['VpcId'])
    except botocore.exceptions.ClientError as e:
        module.fail_json(msg=to_native(e), exception=traceback.format_exc())

    status_achieved, vgw = wait_for_status(client, module, [vpn_gateway_id], 'attached')
    if not status_achieved:
        module.fail_json(msg='Error waiting for vpc to attach to vgw - please check the AWS console')

    result = response
    return result
Exemplo n.º 21
0
    def __init__(self, module):
        self.module = module

        try:
            region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module, boto3=True)
            self.client = boto3_conn(module, conn_type='client',
                                     resource='cloudformation', region=region,
                                     endpoint=ec2_url, **aws_connect_kwargs)
            backoff_wrapper = AWSRetry.jittered_backoff(retries=10, delay=3, max_delay=30)
            self.client.describe_stacks = backoff_wrapper(self.client.describe_stacks)
            self.client.list_stack_resources = backoff_wrapper(self.client.list_stack_resources)
            self.client.describe_stack_events = backoff_wrapper(self.client.describe_stack_events)
            self.client.get_stack_policy = backoff_wrapper(self.client.get_stack_policy)
            self.client.get_template = backoff_wrapper(self.client.get_template)
        except botocore.exceptions.NoRegionError:
            self.module.fail_json(msg="Region must be specified as a parameter, in AWS_DEFAULT_REGION environment variable or in boto configuration file")
        except Exception as e:
            self.module.fail_json(msg="Can't establish connection - " + str(e), exception=traceback.format_exc())
def create_connection(client, location, bandwidth, name, lag_id):
    if not name:
        raise DirectConnectError(msg="Failed to create a Direct Connect connection: name required.")
    params = {
        'location': location,
        'bandwidth': bandwidth,
        'connectionName': name,
    }
    if lag_id:
        params['lagId'] = lag_id

    try:
        connection = AWSRetry.backoff(**retry_params)(client.create_connection)(**params)
    except (BotoCoreError, ClientError) as e:
        raise DirectConnectError(msg="Failed to create DirectConnect connection {0}".format(name),
                                 last_traceback=traceback.format_exc(),
                                 exception=e)
    return connection['connectionId']
Exemplo n.º 23
0
    def get_elb_attributes(self):
        """
        Get load balancer attributes

        :return:
        """

        try:
            attr_list = AWSRetry.jittered_backoff()(
                self.connection.describe_load_balancer_attributes
            )(LoadBalancerArn=self.elb['LoadBalancerArn'])['Attributes']

            elb_attributes = boto3_tag_list_to_ansible_dict(attr_list)
        except (BotoCoreError, ClientError) as e:
            self.module.fail_json_aws(e)

        # Replace '.' with '_' in attribute key names to make it more Ansibley
        return dict((k.replace('.', '_'), v) for k, v in elb_attributes.items())
Exemplo n.º 24
0
def convert_tg_name_to_arn(connection, module, tg_name):
    """
    Get ARN of a target group using the target group's name

    :param connection: AWS boto3 elbv2 connection
    :param module: Ansible module
    :param tg_name: Name of the target group
    :return: target group ARN string
    """

    try:
        response = AWSRetry.jittered_backoff()(connection.describe_target_groups)(Names=[tg_name])
    except (BotoCoreError, ClientError) as e:
        module.fail_json_aws(e)

    tg_arn = response['TargetGroups'][0]['TargetGroupArn']

    return tg_arn
Exemplo n.º 25
0
def get_vpc(module, connection, vpc_id):
    # wait for vpc to be available
    try:
        connection.get_waiter('vpc_available').wait(VpcIds=[vpc_id])
    except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
        module.fail_json_aws(e, msg="Unable to wait for VPC {0} to be available.".format(vpc_id))

    try:
        vpc_obj = AWSRetry.backoff(
            delay=3, tries=8,
            catch_extra_error_codes=['InvalidVpcID.NotFound'],
        )(connection.describe_vpcs)(VpcIds=[vpc_id])['Vpcs'][0]
    except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
        module.fail_json_aws(e, msg="Failed to describe VPCs")
    try:
        vpc_obj['ClassicLinkEnabled'] = get_classic_link_with_backoff(connection, vpc_id)
    except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
        module.fail_json_aws(e, msg="Failed to describe VPCs")

    return vpc_obj
Exemplo n.º 26
0
    def list_elbs(self):
        elb_array, token = [], None
        get_elb_with_backoff = AWSRetry.backoff(tries=5, delay=5, backoff=2.0)(self.connection.get_all_load_balancers)
        while True:
            all_elbs = get_elb_with_backoff(marker=token)
            token = all_elbs.next_marker

            if all_elbs:
                if self.names:
                    for existing_lb in all_elbs:
                        if existing_lb.name in self.names:
                            elb_array.append(existing_lb)
                else:
                    elb_array.extend(all_elbs)
            else:
                break

            if token is None:
                break

        return list(map(self._get_elb_info, elb_array))
def connection_exists(client, connection_id=None, connection_name=None, verify=True):
    params = {}
    if connection_id:
        params['connectionId'] = connection_id
    try:
        response = AWSRetry.backoff(**retry_params)(client.describe_connections)(**params)
    except (BotoCoreError, ClientError) as e:
        if connection_id:
            msg = "Failed to describe DirectConnect ID {0}".format(connection_id)
        else:
            msg = "Failed to describe DirectConnect connections"
        raise DirectConnectError(msg=msg,
                                 last_traceback=traceback.format_exc(),
                                 exception=e)

    match = []
    connection = []

    # look for matching connections

    if len(response.get('connections', [])) == 1 and connection_id:
        if response['connections'][0]['connectionState'] != 'deleted':
            match.append(response['connections'][0]['connectionId'])
            connection.extend(response['connections'])

    for conn in response.get('connections', []):
        if connection_name == conn['connectionName'] and conn['connectionState'] != 'deleted':
            match.append(conn['connectionId'])
            connection.append(conn)

    # verifying if the connections exists; if true, return connection identifier, otherwise return False
    if verify and len(match) == 1:
        return match[0]
    elif verify:
        return False
    # not verifying if the connection exists; just return current connection info
    elif len(connection) == 1:
        return {'connection': connection[0]}
    return {'connection': {}}
Exemplo n.º 28
0
def main():
    module = AnsibleAWSModule(
        argument_spec={
            'identity': dict(required=True, type='str'),
            'state': dict(default='present', choices=['present', 'absent']),
            'policy_name': dict(required=True, type='str'),
            'policy': dict(type='json', default=None),
        },
        required_if=[['state', 'present', ['policy']]],
        supports_check_mode=True,
    )

    # SES APIs seem to have a much lower throttling threshold than most of the rest of the AWS APIs.
    # Docs say 1 call per second. This shouldn't actually be a big problem for normal usage, but
    # the ansible build runs multiple instances of the test in parallel that's caused throttling
    # failures so apply a jittered backoff to call SES calls.
    connection = module.client('ses', retry_decorator=AWSRetry.jittered_backoff())

    state = module.params.get("state")

    if state == 'present':
        create_or_update_identity_policy(connection, module)
    else:
        delete_identity_policy(connection, module)
Exemplo n.º 29
0
def main():
    argument_spec = ansible.module_utils.ec2.ec2_argument_spec()
    argument_spec.update(dict(
        stack_name=dict(required=True),
        template_parameters=dict(required=False, type='dict', default={}),
        state=dict(default='present', choices=['present', 'absent']),
        template=dict(default=None, required=False, type='path'),
        notification_arns=dict(default=None, required=False),
        stack_policy=dict(default=None, required=False),
        disable_rollback=dict(default=False, type='bool'),
        template_url=dict(default=None, required=False),
        template_format=dict(default=None, choices=['json', 'yaml'], required=False),
        create_changeset=dict(default=False, type='bool'),
        changeset_name=dict(default=None, required=False),
        role_arn=dict(default=None, required=False),
        tags=dict(default=None, type='dict')
    )
    )

    module = AnsibleModule(
        argument_spec=argument_spec,
        mutually_exclusive=[['template_url', 'template']],
        supports_check_mode=True
    )
    if not HAS_BOTO3:
        module.fail_json(msg='boto3 and botocore are required for this module')

    # collect the parameters that are passed to boto3. Keeps us from having so many scalars floating around.
    stack_params = {
        'Capabilities': ['CAPABILITY_IAM', 'CAPABILITY_NAMED_IAM'],
    }
    state = module.params['state']
    stack_params['StackName'] = module.params['stack_name']

    if module.params['template'] is not None:
        stack_params['TemplateBody'] = open(module.params['template'], 'r').read()
    elif module.params['template_url'] is not None:
        stack_params['TemplateURL'] = module.params['template_url']

    if module.params.get('notification_arns'):
        stack_params['NotificationARNs'] = module.params['notification_arns'].split(',')
    else:
        stack_params['NotificationARNs'] = []

    if module.params['stack_policy'] is not None:
        stack_params['StackPolicyBody'] = open(module.params['stack_policy'], 'r').read()

    if module.params['changeset_name'] is not None:
        stack_params['ChangeSetName'] = module.params['changeset_name']

    template_parameters = module.params['template_parameters']
    stack_params['Parameters'] = [{'ParameterKey':k, 'ParameterValue':str(v)} for k, v in template_parameters.items()]

    if isinstance(module.params.get('tags'), dict):
        stack_params['Tags'] = ansible.module_utils.ec2.ansible_dict_to_boto3_tag_list(module.params['tags'])

    if module.params.get('role_arn'):
        stack_params['RoleARN'] = module.params['role_arn']

    result = {}

    try:
        region, ec2_url, aws_connect_kwargs = ansible.module_utils.ec2.get_aws_connection_info(module, boto3=True)
        cfn = ansible.module_utils.ec2.boto3_conn(module, conn_type='client', resource='cloudformation', region=region, endpoint=ec2_url, **aws_connect_kwargs)
    except botocore.exceptions.NoCredentialsError as e:
        module.fail_json(msg=boto_exception(e))

    # Wrap the cloudformation client methods that this module uses with
    # automatic backoff / retry for throttling error codes
    backoff_wrapper = AWSRetry.jittered_backoff(retries=10, delay=3, max_delay=30)
    cfn.describe_stack_events = backoff_wrapper(cfn.describe_stack_events)
    cfn.create_stack = backoff_wrapper(cfn.create_stack)
    cfn.list_change_sets = backoff_wrapper(cfn.list_change_sets)
    cfn.create_change_set = backoff_wrapper(cfn.create_change_set)
    cfn.update_stack = backoff_wrapper(cfn.update_stack)
    cfn.describe_stacks = backoff_wrapper(cfn.describe_stacks)
    cfn.list_stack_resources = backoff_wrapper(cfn.list_stack_resources)
    cfn.delete_stack = backoff_wrapper(cfn.delete_stack)

    stack_info = get_stack_facts(cfn, stack_params['StackName'])

    if module.check_mode:
        if state == 'absent' and stack_info:
            module.exit_json(changed=True, msg='Stack would be deleted', meta=[])
        elif state == 'absent' and not stack_info:
            module.exit_json(changed=False, msg='Stack doesn\'t exist', meta=[])
        elif state == 'present' and not stack_info:
            module.exit_json(changed=True, msg='New stack would be created', meta=[])
        else:
            module.exit_json(**check_mode_changeset(module, stack_params, cfn))

    if state == 'present':
        if not stack_info:
            result = create_stack(module, stack_params, cfn)
        elif module.params.get('create_changeset'):
            result = create_changeset(module, stack_params, cfn)
        else:
            result = update_stack(module, stack_params, cfn)

        # format the stack output

        stack = get_stack_facts(cfn, stack_params['StackName'])
        if result.get('stack_outputs') is None:
            # always define stack_outputs, but it may be empty
            result['stack_outputs'] = {}
        for output in stack.get('Outputs', []):
            result['stack_outputs'][output['OutputKey']] = output['OutputValue']
        stack_resources = []
        reslist = cfn.list_stack_resources(StackName=stack_params['StackName'])
        for res in reslist.get('StackResourceSummaries', []):
            stack_resources.append({
                "logical_resource_id": res['LogicalResourceId'],
                "physical_resource_id": res.get('PhysicalResourceId', ''),
                "resource_type": res['ResourceType'],
                "last_updated_time": res['LastUpdatedTimestamp'],
                "status": res['ResourceStatus'],
                "status_reason": res.get('ResourceStatusReason') # can be blank, apparently
            })
        result['stack_resources'] = stack_resources

    elif state == 'absent':
        # absent state is different because of the way delete_stack works.
        # problem is it it doesn't give an error if stack isn't found
        # so must describe the stack first

        try:
            stack = get_stack_facts(cfn, stack_params['StackName'])
            if not stack:
                result = {'changed': False, 'output': 'Stack not found.'}
            else:
                cfn.delete_stack(StackName=stack_params['StackName'])
                result = stack_operation(cfn, stack_params['StackName'], 'DELETE')
        except Exception as err:
            module.fail_json(msg=boto_exception(err), exception=traceback.format_exc())

    if module.params['template_format'] is not None:
        result['warnings'] = [('Argument `template_format` is deprecated '
            'since Ansible 2.3, JSON and YAML templates are now passed '
            'directly to the CloudFormation API.')]
    module.exit_json(**result)
Exemplo n.º 30
0
def main():
    module = AnsibleAWSModule(
        argument_spec={
            'name':
            dict(type='str', required=True),
            'state':
            dict(type='str', choices=['present', 'absent'], default='present'),
            'description':
            dict(type='str'),
            'scope':
            dict(type='dict'),
            'source':
            dict(type='dict', required=True),
            'input_parameters':
            dict(type='str'),
            'execution_frequency':
            dict(type='str',
                 choices=[
                     'One_Hour', 'Three_Hours', 'Six_Hours', 'Twelve_Hours',
                     'TwentyFour_Hours'
                 ]),
        },
        supports_check_mode=False,
    )

    result = {'changed': False}

    name = module.params.get('name')
    resource_type = module.params.get('resource_type')
    state = module.params.get('state')

    params = {}
    if name:
        params['ConfigRuleName'] = name
    if module.params.get('description'):
        params['Description'] = module.params.get('description')
    if module.params.get('scope'):
        params['Scope'] = {}
        if module.params.get('scope').get('compliance_types'):
            params['Scope'].update({
                'ComplianceResourceTypes':
                module.params.get('scope').get('compliance_types')
            })
        if module.params.get('scope').get('tag_key'):
            params['Scope'].update(
                {'TagKey': module.params.get('scope').get('tag_key')})
        if module.params.get('scope').get('tag_value'):
            params['Scope'].update(
                {'TagValue': module.params.get('scope').get('tag_value')})
        if module.params.get('scope').get('compliance_id'):
            params['Scope'].update({
                'ComplianceResourceId':
                module.params.get('scope').get('compliance_id')
            })
    if module.params.get('source'):
        params['Source'] = {}
        if module.params.get('source').get('owner'):
            params['Source'].update(
                {'Owner': module.params.get('source').get('owner')})
        if module.params.get('source').get('identifier'):
            params['Source'].update({
                'SourceIdentifier':
                module.params.get('source').get('identifier')
            })
        if module.params.get('source').get('details'):
            params['Source'].update(
                {'SourceDetails': module.params.get('source').get('details')})
    if module.params.get('input_parameters'):
        params['InputParameters'] = module.params.get('input_parameters')
    if module.params.get('execution_frequency'):
        params['MaximumExecutionFrequency'] = module.params.get(
            'execution_frequency')
    params['ConfigRuleState'] = 'ACTIVE'

    client = module.client('config',
                           retry_decorator=AWSRetry.jittered_backoff())

    existing_rule = rule_exists(client, module, params)

    if state == 'present':
        if not existing_rule:
            create_resource(client, module, params, result)
        else:
            update_resource(client, module, params, result)

    if state == 'absent':
        if existing_rule:
            delete_resource(client, module, params, result)

    module.exit_json(**result)
Exemplo n.º 31
0
def main():
    argument_spec = ansible.module_utils.ec2.ec2_argument_spec()
    argument_spec.update(
        dict(
            stack_name=dict(required=True),
            template_parameters=dict(required=False, type='dict', default={}),
            state=dict(default='present', choices=['present', 'absent']),
            template=dict(default=None, required=False, type='path'),
            notification_arns=dict(default=None, required=False),
            stack_policy=dict(default=None, required=False),
            disable_rollback=dict(default=False, type='bool'),
            create_timeout=dict(default=None, type='int'),
            template_url=dict(default=None, required=False),
            template_body=dict(default=None, require=False),
            template_format=dict(default=None,
                                 choices=['json', 'yaml'],
                                 required=False),
            create_changeset=dict(default=False, type='bool'),
            changeset_name=dict(default=None, required=False),
            role_arn=dict(default=None, required=False),
            tags=dict(default=None, type='dict'),
            termination_protection=dict(default=None, type='bool'),
            events_limit=dict(default=200, type='int'),
        ))

    module = AnsibleModule(
        argument_spec=argument_spec,
        mutually_exclusive=[['template_url', 'template', 'template_body']],
        supports_check_mode=True)
    if not HAS_BOTO3:
        module.fail_json(msg='boto3 and botocore are required for this module')

    # collect the parameters that are passed to boto3. Keeps us from having so many scalars floating around.
    stack_params = {
        'Capabilities': ['CAPABILITY_IAM', 'CAPABILITY_NAMED_IAM'],
        'ClientRequestToken': to_native(uuid.uuid4()),
    }
    state = module.params['state']
    stack_params['StackName'] = module.params['stack_name']

    if module.params['template'] is not None:
        stack_params['TemplateBody'] = open(module.params['template'],
                                            'r').read()
    elif module.params['template_body'] is not None:
        stack_params['TemplateBody'] = module.params['template_body']
    elif module.params['template_url'] is not None:
        stack_params['TemplateURL'] = module.params['template_url']

    if module.params.get('notification_arns'):
        stack_params['NotificationARNs'] = module.params[
            'notification_arns'].split(',')
    else:
        stack_params['NotificationARNs'] = []

    # can't check the policy when verifying.
    if module.params[
            'stack_policy'] is not None and not module.check_mode and not module.params[
                'create_changeset']:
        stack_params['StackPolicyBody'] = open(module.params['stack_policy'],
                                               'r').read()

    template_parameters = module.params['template_parameters']

    stack_params['Parameters'] = []
    for k, v in template_parameters.items():
        if isinstance(v, dict):
            # set parameter based on a dict to allow additional CFN Parameter Attributes
            param = dict(ParameterKey=k)

            if 'value' in v:
                param['ParameterValue'] = str(v['value'])

            if 'use_previous_value' in v and bool(v['use_previous_value']):
                param['UsePreviousValue'] = True
                param.pop('ParameterValue', None)

            stack_params['Parameters'].append(param)
        else:
            # allow default k/v configuration to set a template parameter
            stack_params['Parameters'].append({
                'ParameterKey': k,
                'ParameterValue': str(v)
            })

    if isinstance(module.params.get('tags'), dict):
        stack_params[
            'Tags'] = ansible.module_utils.ec2.ansible_dict_to_boto3_tag_list(
                module.params['tags'])

    if module.params.get('role_arn'):
        stack_params['RoleARN'] = module.params['role_arn']

    result = {}

    try:
        region, ec2_url, aws_connect_kwargs = ansible.module_utils.ec2.get_aws_connection_info(
            module, boto3=True)
        cfn = ansible.module_utils.ec2.boto3_conn(module,
                                                  conn_type='client',
                                                  resource='cloudformation',
                                                  region=region,
                                                  endpoint=ec2_url,
                                                  **aws_connect_kwargs)
    except botocore.exceptions.NoCredentialsError as e:
        module.fail_json(msg=boto_exception(e))

    # Wrap the cloudformation client methods that this module uses with
    # automatic backoff / retry for throttling error codes
    backoff_wrapper = AWSRetry.jittered_backoff(retries=10,
                                                delay=3,
                                                max_delay=30)
    cfn.describe_stack_events = backoff_wrapper(cfn.describe_stack_events)
    cfn.create_stack = backoff_wrapper(cfn.create_stack)
    cfn.list_change_sets = backoff_wrapper(cfn.list_change_sets)
    cfn.create_change_set = backoff_wrapper(cfn.create_change_set)
    cfn.update_stack = backoff_wrapper(cfn.update_stack)
    cfn.describe_stacks = backoff_wrapper(cfn.describe_stacks)
    cfn.list_stack_resources = backoff_wrapper(cfn.list_stack_resources)
    cfn.delete_stack = backoff_wrapper(cfn.delete_stack)
    if boto_supports_termination_protection(cfn):
        cfn.update_termination_protection = backoff_wrapper(
            cfn.update_termination_protection)

    stack_info = get_stack_facts(cfn, stack_params['StackName'])

    if module.check_mode:
        if state == 'absent' and stack_info:
            module.exit_json(changed=True,
                             msg='Stack would be deleted',
                             meta=[])
        elif state == 'absent' and not stack_info:
            module.exit_json(changed=False,
                             msg='Stack doesn\'t exist',
                             meta=[])
        elif state == 'present' and not stack_info:
            module.exit_json(changed=True,
                             msg='New stack would be created',
                             meta=[])
        else:
            module.exit_json(**check_mode_changeset(module, stack_params, cfn))

    if state == 'present':
        if not stack_info:
            result = create_stack(module, stack_params, cfn,
                                  module.params.get('events_limit'))
        elif module.params.get('create_changeset'):
            result = create_changeset(module, stack_params, cfn,
                                      module.params.get('events_limit'))
        else:
            if module.params.get('termination_protection') is not None:
                update_termination_protection(
                    module, cfn, stack_params['StackName'],
                    bool(module.params.get('termination_protection')))
            result = update_stack(module, stack_params, cfn,
                                  module.params.get('events_limit'))

        # format the stack output

        stack = get_stack_facts(cfn, stack_params['StackName'])
        if result.get('stack_outputs') is None:
            # always define stack_outputs, but it may be empty
            result['stack_outputs'] = {}
        for output in stack.get('Outputs', []):
            result['stack_outputs'][
                output['OutputKey']] = output['OutputValue']
        stack_resources = []
        reslist = cfn.list_stack_resources(StackName=stack_params['StackName'])
        for res in reslist.get('StackResourceSummaries', []):
            stack_resources.append({
                "logical_resource_id":
                res['LogicalResourceId'],
                "physical_resource_id":
                res.get('PhysicalResourceId', ''),
                "resource_type":
                res['ResourceType'],
                "last_updated_time":
                res['LastUpdatedTimestamp'],
                "status":
                res['ResourceStatus'],
                "status_reason":
                res.get('ResourceStatusReason')  # can be blank, apparently
            })
        result['stack_resources'] = stack_resources

    elif state == 'absent':
        # absent state is different because of the way delete_stack works.
        # problem is it it doesn't give an error if stack isn't found
        # so must describe the stack first

        try:
            stack = get_stack_facts(cfn, stack_params['StackName'])
            if not stack:
                result = {'changed': False, 'output': 'Stack not found.'}
            else:
                if stack_params.get('RoleARN') is None:
                    cfn.delete_stack(StackName=stack_params['StackName'])
                else:
                    cfn.delete_stack(StackName=stack_params['StackName'],
                                     RoleARN=stack_params['RoleARN'])
                result = stack_operation(
                    cfn, stack_params['StackName'], 'DELETE',
                    module.params.get('events_limit'),
                    stack_params.get('ClientRequestToken', None))
        except Exception as err:
            module.fail_json(msg=boto_exception(err),
                             exception=traceback.format_exc())

    if module.params['template_format'] is not None:
        result['warnings'] = [
            ('Argument `template_format` is deprecated '
             'since Ansible 2.3, JSON and YAML templates are now passed '
             'directly to the CloudFormation API.')
        ]
    module.exit_json(**result)
Exemplo n.º 32
0
def main():
    argument_spec = dict(
        name=dict(required=True),
        cidr_block=dict(type='list', required=True),
        ipv6_cidr=dict(type='bool', default=False),
        tenancy=dict(choices=['default', 'dedicated'], default='default'),
        dns_support=dict(type='bool', default=True),
        dns_hostnames=dict(type='bool', default=True),
        dhcp_opts_id=dict(),
        tags=dict(type='dict', aliases=['resource_tags']),
        state=dict(choices=['present', 'absent'], default='present'),
        multi_ok=dict(type='bool', default=False),
        purge_cidrs=dict(type='bool', default=False),
    )

    module = AnsibleAWSModule(argument_spec=argument_spec,
                              supports_check_mode=True)

    name = module.params.get('name')
    cidr_block = get_cidr_network_bits(module, module.params.get('cidr_block'))
    ipv6_cidr = module.params.get('ipv6_cidr')
    purge_cidrs = module.params.get('purge_cidrs')
    tenancy = module.params.get('tenancy')
    dns_support = module.params.get('dns_support')
    dns_hostnames = module.params.get('dns_hostnames')
    dhcp_id = module.params.get('dhcp_opts_id')
    tags = module.params.get('tags')
    state = module.params.get('state')
    multi = module.params.get('multi_ok')

    changed = False

    connection = module.client(
        'ec2',
        retry_decorator=AWSRetry.jittered_backoff(
            retries=8,
            delay=3,
            catch_extra_error_codes=['InvalidVpcID.NotFound']))

    if dns_hostnames and not dns_support:
        module.fail_json(
            msg=
            'In order to enable DNS Hostnames you must also enable DNS support'
        )

    if state == 'present':

        # Check if VPC exists
        vpc_id = vpc_exists(module, connection, name, cidr_block, multi)

        if vpc_id is None:
            vpc_id = create_vpc(connection, module, cidr_block[0], tenancy)
            changed = True

        vpc_obj = get_vpc(module, connection, vpc_id)

        associated_cidrs = dict(
            (cidr['CidrBlock'], cidr['AssociationId'])
            for cidr in vpc_obj.get('CidrBlockAssociationSet', [])
            if cidr['CidrBlockState']['State'] != 'disassociated')
        to_add = [cidr for cidr in cidr_block if cidr not in associated_cidrs]
        to_remove = [
            associated_cidrs[cidr] for cidr in associated_cidrs
            if cidr not in cidr_block
        ]
        expected_cidrs = [
            cidr for cidr in associated_cidrs
            if associated_cidrs[cidr] not in to_remove
        ] + to_add

        if len(cidr_block) > 1:
            for cidr in to_add:
                changed = True
                try:
                    connection.associate_vpc_cidr_block(CidrBlock=cidr,
                                                        VpcId=vpc_id)
                except (botocore.exceptions.ClientError,
                        botocore.exceptions.BotoCoreError) as e:
                    module.fail_json_aws(
                        e, "Unable to associate CIDR {0}.".format(ipv6_cidr))
        if ipv6_cidr:
            if 'Ipv6CidrBlockAssociationSet' in vpc_obj.keys():
                module.warn(
                    "Only one IPv6 CIDR is permitted per VPC, {0} already has CIDR {1}"
                    .format(
                        vpc_id, vpc_obj['Ipv6CidrBlockAssociationSet'][0]
                        ['Ipv6CidrBlock']))
            else:
                try:
                    connection.associate_vpc_cidr_block(
                        AmazonProvidedIpv6CidrBlock=ipv6_cidr, VpcId=vpc_id)
                    changed = True
                except (botocore.exceptions.ClientError,
                        botocore.exceptions.BotoCoreError) as e:
                    module.fail_json_aws(
                        e, "Unable to associate CIDR {0}.".format(ipv6_cidr))

        if purge_cidrs:
            for association_id in to_remove:
                changed = True
                try:
                    connection.disassociate_vpc_cidr_block(
                        AssociationId=association_id)
                except (botocore.exceptions.ClientError,
                        botocore.exceptions.BotoCoreError) as e:
                    module.fail_json_aws(
                        e,
                        "Unable to disassociate {0}. You must detach or delete all gateways and resources that "
                        "are associated with the CIDR block before you can disassociate it."
                        .format(association_id))

        if dhcp_id is not None:
            try:
                if update_dhcp_opts(connection, module, vpc_obj, dhcp_id):
                    changed = True
            except (botocore.exceptions.ClientError,
                    botocore.exceptions.BotoCoreError) as e:
                module.fail_json_aws(e, "Failed to update DHCP options")

        if tags is not None or name is not None:
            try:
                if update_vpc_tags(connection, module, vpc_id, tags, name):
                    changed = True
            except (botocore.exceptions.ClientError,
                    botocore.exceptions.BotoCoreError) as e:
                module.fail_json_aws(e, msg="Failed to update tags")

        current_dns_enabled = connection.describe_vpc_attribute(
            Attribute='enableDnsSupport', VpcId=vpc_id,
            aws_retry=True)['EnableDnsSupport']['Value']
        current_dns_hostnames = connection.describe_vpc_attribute(
            Attribute='enableDnsHostnames', VpcId=vpc_id,
            aws_retry=True)['EnableDnsHostnames']['Value']
        if current_dns_enabled != dns_support:
            changed = True
            if not module.check_mode:
                try:
                    connection.modify_vpc_attribute(
                        VpcId=vpc_id, EnableDnsSupport={'Value': dns_support})
                except (botocore.exceptions.ClientError,
                        botocore.exceptions.BotoCoreError) as e:
                    module.fail_json_aws(
                        e, "Failed to update enabled dns support attribute")
        if current_dns_hostnames != dns_hostnames:
            changed = True
            if not module.check_mode:
                try:
                    connection.modify_vpc_attribute(
                        VpcId=vpc_id,
                        EnableDnsHostnames={'Value': dns_hostnames})
                except (botocore.exceptions.ClientError,
                        botocore.exceptions.BotoCoreError) as e:
                    module.fail_json_aws(
                        e, "Failed to update enabled dns hostnames attribute")

        # wait for associated cidrs to match
        if to_add or to_remove:
            try:
                connection.get_waiter('vpc_available').wait(
                    VpcIds=[vpc_id],
                    Filters=[{
                        'Name': 'cidr-block-association.cidr-block',
                        'Values': expected_cidrs
                    }])
            except (botocore.exceptions.ClientError,
                    botocore.exceptions.BotoCoreError) as e:
                module.fail_json_aws(e, "Failed to wait for CIDRs to update")

        # try to wait for enableDnsSupport and enableDnsHostnames to match
        wait_for_vpc_attribute(connection, module, vpc_id, 'enableDnsSupport',
                               dns_support)
        wait_for_vpc_attribute(connection, module, vpc_id,
                               'enableDnsHostnames', dns_hostnames)

        final_state = camel_dict_to_snake_dict(
            get_vpc(module, connection, vpc_id))
        final_state['tags'] = boto3_tag_list_to_ansible_dict(
            final_state.get('tags', []))
        final_state['id'] = final_state.pop('vpc_id')

        module.exit_json(changed=changed, vpc=final_state)

    elif state == 'absent':

        # Check if VPC exists
        vpc_id = vpc_exists(module, connection, name, cidr_block, multi)

        if vpc_id is not None:
            try:
                if not module.check_mode:
                    connection.delete_vpc(VpcId=vpc_id)
                changed = True
            except (botocore.exceptions.ClientError,
                    botocore.exceptions.BotoCoreError) as e:
                module.fail_json_aws(
                    e,
                    msg=
                    "Failed to delete VPC {0} You may want to use the ec2_vpc_subnet, ec2_vpc_igw, "
                    "and/or ec2_vpc_route_table modules to ensure the other components are absent."
                    .format(vpc_id))

        module.exit_json(changed=changed, vpc={})
Exemplo n.º 33
0
def main():
    argument_spec = dict(
        name=dict(required=True),
        cidr_block=dict(type='list', required=True),
        tenancy=dict(choices=['default', 'dedicated'], default='default'),
        dns_support=dict(type='bool', default=True),
        dns_hostnames=dict(type='bool', default=True),
        dhcp_opts_id=dict(),
        tags=dict(type='dict', aliases=['resource_tags']),
        state=dict(choices=['present', 'absent'], default='present'),
        multi_ok=dict(type='bool', default=False),
        purge_cidrs=dict(type='bool', default=False),
    )

    module = AnsibleAWSModule(
        argument_spec=argument_spec,
        supports_check_mode=True
    )

    name = module.params.get('name')
    cidr_block = module.params.get('cidr_block')
    purge_cidrs = module.params.get('purge_cidrs')
    tenancy = module.params.get('tenancy')
    dns_support = module.params.get('dns_support')
    dns_hostnames = module.params.get('dns_hostnames')
    dhcp_id = module.params.get('dhcp_opts_id')
    tags = module.params.get('tags')
    state = module.params.get('state')
    multi = module.params.get('multi_ok')

    changed = False

    connection = module.client(
        'ec2',
        retry_decorator=AWSRetry.jittered_backoff(
            retries=8, delay=3, catch_extra_error_codes=['InvalidVpcID.NotFound']
        )
    )

    if dns_hostnames and not dns_support:
        module.fail_json(msg='In order to enable DNS Hostnames you must also enable DNS support')

    if state == 'present':

        # Check if VPC exists
        vpc_id = vpc_exists(module, connection, name, cidr_block, multi)

        if vpc_id is None:
            vpc_id = create_vpc(connection, module, cidr_block[0], tenancy)
            changed = True

        vpc_obj = get_vpc(module, connection, vpc_id)

        associated_cidrs = dict((cidr['CidrBlock'], cidr['AssociationId']) for cidr in vpc_obj.get('CidrBlockAssociationSet', [])
                                if cidr['CidrBlockState']['State'] != 'disassociated')
        to_add = [cidr for cidr in cidr_block if cidr not in associated_cidrs]
        to_remove = [associated_cidrs[cidr] for cidr in associated_cidrs if cidr not in cidr_block]
        expected_cidrs = [cidr for cidr in associated_cidrs if associated_cidrs[cidr] not in to_remove] + to_add

        if len(cidr_block) > 1:
            for cidr in to_add:
                changed = True
                connection.associate_vpc_cidr_block(CidrBlock=cidr, VpcId=vpc_id)

        if purge_cidrs:
            for association_id in to_remove:
                changed = True
                try:
                    connection.disassociate_vpc_cidr_block(AssociationId=association_id)
                except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
                    module.fail_json_aws(e, "Unable to disassociate {0}. You must detach or delete all gateways and resources that "
                                         "are associated with the CIDR block before you can disassociate it.".format(association_id))

        if dhcp_id is not None:
            try:
                if update_dhcp_opts(connection, module, vpc_obj, dhcp_id):
                    changed = True
            except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
                module.fail_json_aws(e, "Failed to update DHCP options")

        if tags is not None or name is not None:
            try:
                if update_vpc_tags(connection, module, vpc_id, tags, name):
                    changed = True
            except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
                module.fail_json_aws(e, msg="Failed to update tags")

        current_dns_enabled = connection.describe_vpc_attribute(Attribute='enableDnsSupport', VpcId=vpc_id, aws_retry=True)['EnableDnsSupport']['Value']
        current_dns_hostnames = connection.describe_vpc_attribute(Attribute='enableDnsHostnames', VpcId=vpc_id, aws_retry=True)['EnableDnsHostnames']['Value']
        if current_dns_enabled != dns_support:
            changed = True
            if not module.check_mode:
                try:
                    connection.modify_vpc_attribute(VpcId=vpc_id, EnableDnsSupport={'Value': dns_support})
                except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
                    module.fail_json_aws(e, "Failed to update enabled dns support attribute")
        if current_dns_hostnames != dns_hostnames:
            changed = True
            if not module.check_mode:
                try:
                    connection.modify_vpc_attribute(VpcId=vpc_id, EnableDnsHostnames={'Value': dns_hostnames})
                except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
                    module.fail_json_aws(e, "Failed to update enabled dns hostnames attribute")

        # wait for associated cidrs to match
        if to_add or to_remove:
            try:
                connection.get_waiter('vpc_available').wait(
                    VpcIds=[vpc_id],
                    Filters=[{'Name': 'cidr-block-association.cidr-block', 'Values': expected_cidrs}]
                )
            except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
                module.fail_json_aws(e, "Failed to wait for CIDRs to update")

        # try to wait for enableDnsSupport and enableDnsHostnames to match
        wait_for_vpc_attribute(connection, module, vpc_id, 'enableDnsSupport', dns_support)
        wait_for_vpc_attribute(connection, module, vpc_id, 'enableDnsHostnames', dns_hostnames)

        final_state = camel_dict_to_snake_dict(get_vpc(module, connection, vpc_id))
        final_state['tags'] = boto3_tag_list_to_ansible_dict(final_state.get('tags', []))
        final_state['id'] = final_state.pop('vpc_id')

        module.exit_json(changed=changed, vpc=final_state)

    elif state == 'absent':

        # Check if VPC exists
        vpc_id = vpc_exists(module, connection, name, cidr_block, multi)

        if vpc_id is not None:
            try:
                if not module.check_mode:
                    connection.delete_vpc(VpcId=vpc_id)
                changed = True
            except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
                module.fail_json_aws(e, msg="Failed to delete VPC {0} You may want to use the ec2_vpc_subnet, ec2_vpc_igw, "
                                     "and/or ec2_vpc_route_table modules to ensure the other components are absent.".format(vpc_id))

        module.exit_json(changed=changed, vpc={})
def create_or_update(module, template_options):
    ec2 = module.client('ec2', retry_decorator=AWSRetry.jittered_backoff())
    template, template_versions = existing_templates(module)
    out = {}
    lt_data = params_to_launch_data(
        module,
        dict(
            (k, v) for k, v in module.params.items() if k in template_options))
    if not (template or template_versions):
        # create a full new one
        try:
            resp = ec2.create_launch_template(
                LaunchTemplateName=module.params['template_name'],
                LaunchTemplateData=lt_data,
                ClientToken=uuid4().hex,
                aws_retry=True,
            )
        except (ClientError, BotoCoreError) as e:
            module.fail_json_aws(e, msg="Couldn't create launch template")
        template, template_versions = existing_templates(module)
        out['changed'] = True
    elif template and template_versions:
        most_recent = sorted(template_versions,
                             key=lambda x: x['VersionNumber'])[-1]
        if lt_data == most_recent['LaunchTemplateData']:
            out['changed'] = False
            return out
        try:
            resp = ec2.create_launch_template_version(
                LaunchTemplateId=template['LaunchTemplateId'],
                LaunchTemplateData=lt_data,
                SourceVersion=module.params.get('source_version'),
                ClientToken=uuid4().hex,
                aws_retry=True,
            )
            if module.params.get('default_version') in (None, ''):
                # no need to do anything, leave the existing version as default
                pass
            elif module.params.get('default_version') == 'latest':
                set_default = ec2.modify_launch_template(
                    LaunchTemplateId=template['LaunchTemplateId'],
                    DefaultVersion=to_text(
                        resp['LaunchTemplateVersion']['VersionNumber']),
                    ClientToken=uuid4().hex,
                    aws_retry=True,
                )
            else:
                try:
                    int(module.params.get('default_version'))
                except ValueError:
                    module.fail_json(
                        msg=
                        'default_version param was not a valid integer, got "{0}"'
                        .format(module.params.get('default_version')))
                set_default = ec2.modify_launch_template(
                    LaunchTemplateId=template['LaunchTemplateId'],
                    DefaultVersion=to_text(
                        int(module.params.get('default_version'))),
                    ClientToken=uuid4().hex,
                    aws_retry=True,
                )
        except (ClientError, BotoCoreError) as e:
            module.fail_json_aws(
                e, msg="Couldn't create subsequent launch template version")
        template, template_versions = existing_templates(module)
        out['changed'] = True
    return out