Server IP : 85.214.239.14 / Your IP : 3.145.101.119 Web Server : Apache/2.4.62 (Debian) System : Linux h2886529.stratoserver.net 4.9.0 #1 SMP Tue Jan 9 19:45:01 MSK 2024 x86_64 User : www-data ( 33) PHP Version : 7.4.18 Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare, MySQL : OFF | cURL : OFF | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : OFF Directory : /lib/python3/dist-packages/ansible_collections/community/aws/plugins/modules/ |
Upload File : |
#!/usr/bin/python # Copyright: Ansible Project # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import absolute_import, division, print_function __metaclass__ = type DOCUMENTATION = r''' --- module: elb_target_group version_added: 1.0.0 short_description: Manage a target group for an Application or Network load balancer description: - Manage an AWS Elastic Load Balancer target group. See U(https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-target-groups.html) or U(https://docs.aws.amazon.com/elasticloadbalancing/latest/network/load-balancer-target-groups.html) for details. author: - "Rob White (@wimnat)" options: deregistration_delay_timeout: description: - The amount time for Elastic Load Balancing to wait before changing the state of a deregistering target from draining to unused. The range is 0-3600 seconds. type: int deregistration_connection_termination: description: - Indicates whether the load balancer terminates connections at the end of the deregistration timeout. - Using this option is only supported when attaching to a Network Load Balancer (NLB). type: bool default: false required: false version_added: 3.1.0 health_check_protocol: description: - The protocol the load balancer uses when performing health checks on targets. required: false choices: [ 'http', 'https', 'tcp', 'tls', 'udp', 'tcp_udp', 'HTTP', 'HTTPS', 'TCP', 'TLS', 'UDP', 'TCP_UDP'] type: str health_check_port: description: - The port the load balancer uses when performing health checks on targets. Can be set to 'traffic-port' to match target port. - When not defined will default to the port on which each target receives traffic from the load balancer. required: false type: str health_check_path: description: - The ping path that is the destination on the targets for health checks. The path must be defined in order to set a health check. - Requires the I(health_check_protocol) parameter to be set. required: false type: str health_check_interval: description: - The approximate amount of time, in seconds, between health checks of an individual target. required: false type: int health_check_timeout: description: - The amount of time, in seconds, during which no response from a target means a failed health check. required: false type: int healthy_threshold_count: description: - The number of consecutive health checks successes required before considering an unhealthy target healthy. required: false type: int modify_targets: description: - Whether or not to alter existing targets in the group to match what is passed with the module required: false default: true type: bool name: description: - The name of the target group. required: true type: str port: description: - The port on which the targets receive traffic. This port is used unless you specify a port override when registering the target. - Required when I(state) is C(present) and I(target_type) is C(instance), C(ip), or C(alb). required: false type: int protocol: description: - The protocol to use for routing traffic to the targets. - Required when I(state) is C(present) and I(target_type) is C(instance), C(ip), or C(alb). required: false choices: [ 'http', 'https', 'tcp', 'tls', 'udp', 'tcp_udp', 'HTTP', 'HTTPS', 'TCP', 'TLS', 'UDP', 'TCP_UDP'] type: str protocol_version: description: - Specifies protocol version. - The protocol_version parameter is immutable and cannot be changed when updating an elb_target_group. required: false choices: ['GRPC', 'HTTP1', 'HTTP2'] type: str version_added: 5.1.0 state: description: - Create or destroy the target group. required: true choices: [ 'present', 'absent' ] type: str stickiness_enabled: description: - Indicates whether sticky sessions are enabled. type: bool stickiness_lb_cookie_duration: description: - The time period, in seconds, during which requests from a client should be routed to the same target. After this time period expires, the load balancer-generated cookie is considered stale. The range is 1 second to 1 week (604800 seconds). type: int stickiness_app_cookie_duration: description: - The time period, in seconds, during which requests from a client should be routed to the same target. After this time period expires, the application-generated cookie is considered stale. The range is 1 second to 1 week (604800 seconds). type: int version_added: 1.5.0 stickiness_app_cookie_name: description: - The name of the application cookie. Required if I(stickiness_type=app_cookie). type: str version_added: 1.5.0 stickiness_type: description: - The type of sticky sessions. - Valid values are C(lb_cookie), C(app_cookie) or C(source_ip). - If not set AWS will default to C(lb_cookie) for Application Load Balancers or C(source_ip) for Network Load Balancers. type: str load_balancing_algorithm_type: description: - The type of load balancing algorithm to use. - Changing the load balancing algorithm is only supported when used with Application Load Balancers (ALB). - If not set AWS will default to C(round_robin). choices: ['round_robin', 'least_outstanding_requests'] type: str version_added: 3.2.0 successful_response_codes: description: - The HTTP codes to use when checking for a successful response from a target. - Accepts multiple values (for example, "200,202") or a range of values (for example, "200-299"). - Requires the I(health_check_protocol) parameter to be set. required: false type: str target_type: description: - The type of target that you must specify when registering targets with this target group. The possible values are C(instance) (targets are specified by instance ID), C(ip) (targets are specified by IP address), C(lambda) (target is specified by ARN), or C(alb) (target is specified by ARN). Note that you can't specify targets for a target group using more than one type. Target types lambda and alb only accept one target. When more than one target is specified, only the first one is used. All additional targets are ignored. If the target type is ip, specify IP addresses from the subnets of the virtual private cloud (VPC) for the target group, the RFC 1918 range (10.0.0.0/8, 172.16.0.0/12, and 192.168.0.0/16), and the RFC 6598 range (100.64.0.0/10). You can't specify publicly routable IP addresses. - The default behavior is C(instance). required: false choices: ['instance', 'ip', 'lambda', 'alb'] type: str targets: description: - A list of targets to assign to the target group. This parameter defaults to an empty list. Unless you set the 'modify_targets' parameter then all existing targets will be removed from the group. The list should be an Id and a Port parameter. See the Examples for detail. required: false type: list elements: dict unhealthy_threshold_count: description: - The number of consecutive health check failures required before considering a target unhealthy. required: false type: int vpc_id: description: - The identifier of the virtual private cloud (VPC). - Required when I(state) is C(present) and I(target_type) is C(instance), C(ip), or C(alb). required: false type: str preserve_client_ip_enabled: description: - Indicates whether client IP preservation is enabled. - The default is disabled if the target group type is C(ip) address and the target group protocol is C(tcp) or C(tls). Otherwise, the default is enabled. Client IP preservation cannot be disabled for C(udp) and C(tcp_udp) target groups. - I(preserve_client_ip_enabled) is supported only by Network Load Balancers. type: bool required: false version_added: 2.1.0 proxy_protocol_v2_enabled: description: - Indicates whether Proxy Protocol version 2 is enabled. - The value is C(true) or C(false). - I(proxy_protocol_v2_enabled) is supported only by Network Load Balancers. type: bool required: false version_added: 2.1.0 wait: description: - Whether or not to wait for the target group. type: bool default: false wait_timeout: description: - The time to wait for the target group. default: 200 type: int extends_documentation_fragment: - amazon.aws.aws - amazon.aws.ec2 - amazon.aws.boto3 - amazon.aws.tags notes: - Once a target group has been created, only its health check can then be modified using subsequent calls ''' EXAMPLES = r''' # Note: These examples do not set authentication details, see the AWS Guide for details. - name: Create a target group with a default health check community.aws.elb_target_group: name: mytargetgroup protocol: http port: 80 vpc_id: vpc-01234567 state: present - name: Create a target group with protocol_version 'GRPC' community.aws.elb_target_group: name: mytargetgroup protocol: http port: 80 vpc_id: vpc-01234567 protocol_version: GRPC state: present - name: Modify the target group with a custom health check community.aws.elb_target_group: name: mytargetgroup protocol: http port: 80 vpc_id: vpc-01234567 health_check_protocol: http health_check_path: /health_check health_check_port: 80 successful_response_codes: 200 health_check_interval: 15 health_check_timeout: 3 healthy_threshold_count: 4 unhealthy_threshold_count: 3 state: present - name: Delete a target group community.aws.elb_target_group: name: mytargetgroup state: absent - name: Create a target group with instance targets community.aws.elb_target_group: name: mytargetgroup protocol: http port: 81 vpc_id: vpc-01234567 health_check_protocol: http health_check_path: / successful_response_codes: "200,250-260" targets: - Id: i-01234567 Port: 80 - Id: i-98765432 Port: 80 state: present wait_timeout: 200 wait: True - name: Create a target group with IP address targets community.aws.elb_target_group: name: mytargetgroup protocol: http port: 81 vpc_id: vpc-01234567 health_check_protocol: http health_check_path: / successful_response_codes: "200,250-260" target_type: ip targets: - Id: 10.0.0.10 Port: 80 AvailabilityZone: all - Id: 10.0.0.20 Port: 80 state: present wait_timeout: 200 wait: True # Using lambda as targets require that the target group # itself is allow to invoke the lambda function. # therefore you need first to create an empty target group # to receive its arn, second, allow the target group # to invoke the lambda function and third, add the target # to the target group - name: first, create empty target group community.aws.elb_target_group: name: my-lambda-targetgroup target_type: lambda state: present modify_targets: False register: out - name: second, allow invoke of the lambda community.aws.lambda_policy: state: "{{ state | default('present') }}" function_name: my-lambda-function statement_id: someID action: lambda:InvokeFunction principal: elasticloadbalancing.amazonaws.com source_arn: "{{ out.target_group_arn }}" - name: third, add target community.aws.elb_target_group: name: my-lambda-targetgroup target_type: lambda state: present targets: - Id: arn:aws:lambda:eu-central-1:123456789012:function:my-lambda-function ''' RETURN = r''' deregistration_delay_timeout_seconds: description: The amount time for Elastic Load Balancing to wait before changing the state of a deregistering target from draining to unused. returned: when state present type: int sample: 300 deregistration_connection_termination: description: Indicates whether the load balancer terminates connections at the end of the deregistration timeout. returned: when state present type: bool sample: True health_check_interval_seconds: description: The approximate amount of time, in seconds, between health checks of an individual target. returned: when state present type: int sample: 30 health_check_path: description: The destination for the health check request. returned: when state present type: str sample: /index.html health_check_port: description: The port to use to connect with the target. returned: when state present type: str sample: traffic-port health_check_protocol: description: The protocol to use to connect with the target. returned: when state present type: str sample: HTTP health_check_timeout_seconds: description: The amount of time, in seconds, during which no response means a failed health check. returned: when state present type: int sample: 5 healthy_threshold_count: description: The number of consecutive health checks successes required before considering an unhealthy target healthy. returned: when state present type: int sample: 5 load_balancer_arns: description: The Amazon Resource Names (ARN) of the load balancers that route traffic to this target group. returned: when state present type: list sample: [] matcher: description: The HTTP codes to use when checking for a successful response from a target. returned: when state present type: dict sample: { "http_code": "200" } port: description: The port on which the targets are listening. returned: when state present type: int sample: 80 protocol: description: The protocol to use for routing traffic to the targets. returned: when state present type: str sample: HTTP stickiness_enabled: description: Indicates whether sticky sessions are enabled. returned: when state present type: bool sample: true stickiness_lb_cookie_duration_seconds: description: The time period, in seconds, during which requests from a client should be routed to the same target. returned: when state present type: int sample: 86400 stickiness_type: description: The type of sticky sessions. returned: when state present type: str sample: lb_cookie load_balancing_algorithm_type: description: The type load balancing algorithm used. returned: when state present type: str version_added: 3.2.0 sample: least_outstanding_requests tags: description: The tags attached to the target group. returned: when state present type: dict sample: "{ 'Tag': 'Example' }" target_group_arn: description: The Amazon Resource Name (ARN) of the target group. returned: when state present type: str sample: "arn:aws:elasticloadbalancing:ap-southeast-2:123456789012:targetgroup/mytargetgroup/aabbccddee0044332211" target_group_name: description: The name of the target group. returned: when state present type: str sample: mytargetgroup unhealthy_threshold_count: description: The number of consecutive health check failures required before considering the target unhealthy. returned: when state present type: int sample: 2 vpc_id: description: The ID of the VPC for the targets. returned: when state present type: str sample: vpc-0123456 ''' import time try: import botocore except ImportError: pass # caught by AnsibleAWSModule from ansible.module_utils.common.dict_transformations import camel_dict_to_snake_dict from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code from ansible_collections.amazon.aws.plugins.module_utils.ec2 import ansible_dict_to_boto3_tag_list from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry from ansible_collections.amazon.aws.plugins.module_utils.ec2 import boto3_tag_list_to_ansible_dict from ansible_collections.amazon.aws.plugins.module_utils.ec2 import compare_aws_tags def get_tg_attributes(connection, module, tg_arn): try: _attributes = connection.describe_target_group_attributes(TargetGroupArn=tg_arn, aws_retry=True) tg_attributes = boto3_tag_list_to_ansible_dict(_attributes['Attributes']) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't get target group attributes") # Replace '.' with '_' in attribute key names to make it more Ansible friendly return dict((k.replace('.', '_'), v) for k, v in tg_attributes.items()) def get_target_group_tags(connection, module, target_group_arn): try: _tags = connection.describe_tags(ResourceArns=[target_group_arn], aws_retry=True) return _tags['TagDescriptions'][0]['Tags'] except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't get target group tags") def get_target_group(connection, module, retry_missing=False): extra_codes = ['TargetGroupNotFound'] if retry_missing else [] try: target_group_paginator = connection.get_paginator('describe_target_groups').paginate(Names=[module.params.get("name")]) jittered_retry = AWSRetry.jittered_backoff(retries=10, catch_extra_error_codes=extra_codes) result = jittered_retry(target_group_paginator.build_full_result)() except is_boto3_error_code('TargetGroupNotFound'): return None except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except module.fail_json_aws(e, msg="Couldn't get target group") return result['TargetGroups'][0] def wait_for_status(connection, module, target_group_arn, targets, status): polling_increment_secs = 5 max_retries = (module.params.get('wait_timeout') // polling_increment_secs) status_achieved = False for x in range(0, max_retries): try: response = connection.describe_target_health(TargetGroupArn=target_group_arn, Targets=targets, aws_retry=True) if response['TargetHealthDescriptions'][0]['TargetHealth']['State'] == status: status_achieved = True break else: time.sleep(polling_increment_secs) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't describe target health") result = response return status_achieved, result def create_or_update_attributes(connection, module, target_group, new_target_group): changed = False target_type = module.params.get("target_type") deregistration_delay_timeout = module.params.get("deregistration_delay_timeout") deregistration_connection_termination = module.params.get("deregistration_connection_termination") stickiness_enabled = module.params.get("stickiness_enabled") stickiness_lb_cookie_duration = module.params.get("stickiness_lb_cookie_duration") stickiness_type = module.params.get("stickiness_type") stickiness_app_cookie_duration = module.params.get("stickiness_app_cookie_duration") stickiness_app_cookie_name = module.params.get("stickiness_app_cookie_name") preserve_client_ip_enabled = module.params.get("preserve_client_ip_enabled") proxy_protocol_v2_enabled = module.params.get("proxy_protocol_v2_enabled") load_balancing_algorithm_type = module.params.get("load_balancing_algorithm_type") # Now set target group attributes update_attributes = [] # Get current attributes current_tg_attributes = get_tg_attributes(connection, module, target_group['TargetGroupArn']) if deregistration_delay_timeout is not None: if str(deregistration_delay_timeout) != current_tg_attributes['deregistration_delay_timeout_seconds']: update_attributes.append({'Key': 'deregistration_delay.timeout_seconds', 'Value': str(deregistration_delay_timeout)}) if deregistration_connection_termination is not None: if deregistration_connection_termination and current_tg_attributes.get('deregistration_delay_connection_termination_enabled') != "true": update_attributes.append({'Key': 'deregistration_delay.connection_termination.enabled', 'Value': 'true'}) if stickiness_enabled is not None: if stickiness_enabled and current_tg_attributes['stickiness_enabled'] != "true": update_attributes.append({'Key': 'stickiness.enabled', 'Value': 'true'}) if stickiness_lb_cookie_duration is not None: if str(stickiness_lb_cookie_duration) != current_tg_attributes['stickiness_lb_cookie_duration_seconds']: update_attributes.append({'Key': 'stickiness.lb_cookie.duration_seconds', 'Value': str(stickiness_lb_cookie_duration)}) if stickiness_type is not None: if stickiness_type != current_tg_attributes.get('stickiness_type'): update_attributes.append({'Key': 'stickiness.type', 'Value': stickiness_type}) if stickiness_app_cookie_name is not None: if stickiness_app_cookie_name != current_tg_attributes.get('stickiness_app_cookie_name'): update_attributes.append({'Key': 'stickiness.app_cookie.cookie_name', 'Value': str(stickiness_app_cookie_name)}) if stickiness_app_cookie_duration is not None: if str(stickiness_app_cookie_duration) != current_tg_attributes['stickiness_app_cookie_duration_seconds']: update_attributes.append({'Key': 'stickiness.app_cookie.duration_seconds', 'Value': str(stickiness_app_cookie_duration)}) if preserve_client_ip_enabled is not None: if target_type not in ('udp', 'tcp_udp'): if str(preserve_client_ip_enabled).lower() != current_tg_attributes.get('preserve_client_ip_enabled'): update_attributes.append({'Key': 'preserve_client_ip.enabled', 'Value': str(preserve_client_ip_enabled).lower()}) if proxy_protocol_v2_enabled is not None: if str(proxy_protocol_v2_enabled).lower() != current_tg_attributes.get('proxy_protocol_v2_enabled'): update_attributes.append({'Key': 'proxy_protocol_v2.enabled', 'Value': str(proxy_protocol_v2_enabled).lower()}) if load_balancing_algorithm_type is not None: if str(load_balancing_algorithm_type) != current_tg_attributes['load_balancing_algorithm_type']: update_attributes.append({'Key': 'load_balancing.algorithm.type', 'Value': str(load_balancing_algorithm_type)}) if update_attributes: try: connection.modify_target_group_attributes(TargetGroupArn=target_group['TargetGroupArn'], Attributes=update_attributes, aws_retry=True) changed = True except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # Something went wrong setting attributes. If this target group was created during this task, delete it to leave a consistent state if new_target_group: connection.delete_target_group(TargetGroupArn=target_group['TargetGroupArn'], aws_retry=True) module.fail_json_aws(e, msg="Couldn't delete target group") return changed def create_or_update_target_group(connection, module): changed = False new_target_group = False params = dict() target_type = module.params.get("target_type") params['Name'] = module.params.get("name") params['TargetType'] = target_type if target_type != "lambda": params['Protocol'] = module.params.get("protocol").upper() if module.params.get('protocol_version') is not None: params['ProtocolVersion'] = module.params.get('protocol_version') params['Port'] = module.params.get("port") params['VpcId'] = module.params.get("vpc_id") tags = module.params.get("tags") purge_tags = module.params.get("purge_tags") health_option_keys = [ "health_check_path", "health_check_protocol", "health_check_interval", "health_check_timeout", "healthy_threshold_count", "unhealthy_threshold_count", "successful_response_codes" ] health_options = any(module.params[health_option_key] is not None for health_option_key in health_option_keys) # Set health check if anything set if health_options: if module.params.get("health_check_protocol") is not None: params['HealthCheckProtocol'] = module.params.get("health_check_protocol").upper() if module.params.get("health_check_port") is not None: params['HealthCheckPort'] = module.params.get("health_check_port") if module.params.get("health_check_interval") is not None: params['HealthCheckIntervalSeconds'] = module.params.get("health_check_interval") if module.params.get("health_check_timeout") is not None: params['HealthCheckTimeoutSeconds'] = module.params.get("health_check_timeout") if module.params.get("healthy_threshold_count") is not None: params['HealthyThresholdCount'] = module.params.get("healthy_threshold_count") if module.params.get("unhealthy_threshold_count") is not None: params['UnhealthyThresholdCount'] = module.params.get("unhealthy_threshold_count") # Only need to check response code and path for http(s) health checks protocol = module.params.get("health_check_protocol") if protocol is not None and protocol.upper() in ['HTTP', 'HTTPS']: if module.params.get("health_check_path") is not None: params['HealthCheckPath'] = module.params.get("health_check_path") if module.params.get("successful_response_codes") is not None: params['Matcher'] = {} code_key = 'HttpCode' protocol_version = module.params.get('protocol_version') if protocol_version is not None and protocol_version.upper() == "GRPC": code_key = 'GrpcCode' params['Matcher'][code_key] = module.params.get("successful_response_codes") # Get target group target_group = get_target_group(connection, module) if target_group: diffs = [param for param in ('Port', 'Protocol', 'VpcId') if target_group.get(param) != params.get(param)] if diffs: module.fail_json(msg="Cannot modify %s parameter(s) for a target group" % ", ".join(diffs)) # Target group exists so check health check parameters match what has been passed health_check_params = dict() # Modify health check if anything set if health_options: # Health check protocol if 'HealthCheckProtocol' in params and target_group['HealthCheckProtocol'] != params['HealthCheckProtocol']: health_check_params['HealthCheckProtocol'] = params['HealthCheckProtocol'] # Health check port if 'HealthCheckPort' in params and target_group['HealthCheckPort'] != params['HealthCheckPort']: health_check_params['HealthCheckPort'] = params['HealthCheckPort'] # Health check interval if 'HealthCheckIntervalSeconds' in params and target_group['HealthCheckIntervalSeconds'] != params['HealthCheckIntervalSeconds']: health_check_params['HealthCheckIntervalSeconds'] = params['HealthCheckIntervalSeconds'] # Health check timeout if 'HealthCheckTimeoutSeconds' in params and target_group['HealthCheckTimeoutSeconds'] != params['HealthCheckTimeoutSeconds']: health_check_params['HealthCheckTimeoutSeconds'] = params['HealthCheckTimeoutSeconds'] # Healthy threshold if 'HealthyThresholdCount' in params and target_group['HealthyThresholdCount'] != params['HealthyThresholdCount']: health_check_params['HealthyThresholdCount'] = params['HealthyThresholdCount'] # Unhealthy threshold if 'UnhealthyThresholdCount' in params and target_group['UnhealthyThresholdCount'] != params['UnhealthyThresholdCount']: health_check_params['UnhealthyThresholdCount'] = params['UnhealthyThresholdCount'] # Only need to check response code and path for http(s) health checks if target_group['HealthCheckProtocol'] in ['HTTP', 'HTTPS']: # Health check path if 'HealthCheckPath' in params and target_group['HealthCheckPath'] != params['HealthCheckPath']: health_check_params['HealthCheckPath'] = params['HealthCheckPath'] # Matcher (successful response codes) # TODO: required and here? if "Matcher" in params: code_key = "HttpCode" if target_group.get("ProtocolVersion") == "GRPC": code_key = "GrpcCode" current_matcher_list = target_group["Matcher"][code_key].split(",") requested_matcher_list = params["Matcher"][code_key].split(",") if set(current_matcher_list) != set(requested_matcher_list): health_check_params['Matcher'] = {} health_check_params['Matcher'][code_key] = ','.join(requested_matcher_list) try: if health_check_params: connection.modify_target_group(TargetGroupArn=target_group['TargetGroupArn'], aws_retry=True, **health_check_params) changed = True except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't update target group") # Do we need to modify targets? if module.params.get("modify_targets"): # get list of current target instances. I can't see anything like a describe targets in the doco so # describe_target_health seems to be the only way to get them try: current_targets = connection.describe_target_health( TargetGroupArn=target_group['TargetGroupArn'], aws_retry=True) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't get target group health") if module.params.get("targets"): if target_type != "lambda": params['Targets'] = module.params.get("targets") # Correct type of target ports for target in params['Targets']: target['Port'] = int(target.get('Port', module.params.get('port'))) current_instance_ids = [] for instance in current_targets['TargetHealthDescriptions']: current_instance_ids.append(instance['Target']['Id']) new_instance_ids = [] for instance in params['Targets']: new_instance_ids.append(instance['Id']) add_instances = set(new_instance_ids) - set(current_instance_ids) if add_instances: instances_to_add = [] for target in params["Targets"]: if target["Id"] in add_instances: tmp_item = {"Id": target["Id"], "Port": target["Port"]} if target.get("AvailabilityZone"): tmp_item["AvailabilityZone"] = target["AvailabilityZone"] instances_to_add.append(tmp_item) changed = True try: connection.register_targets(TargetGroupArn=target_group['TargetGroupArn'], Targets=instances_to_add, aws_retry=True) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't register targets") if module.params.get("wait"): status_achieved, registered_instances = wait_for_status( connection, module, target_group['TargetGroupArn'], instances_to_add, 'healthy') if not status_achieved: module.fail_json( msg='Error waiting for target registration to be healthy - please check the AWS console') remove_instances = set(current_instance_ids) - set(new_instance_ids) if remove_instances: instances_to_remove = [] for target in current_targets['TargetHealthDescriptions']: if target['Target']['Id'] in remove_instances: instances_to_remove.append({'Id': target['Target']['Id'], 'Port': target['Target']['Port']}) changed = True try: connection.deregister_targets(TargetGroupArn=target_group['TargetGroupArn'], Targets=instances_to_remove, aws_retry=True) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't remove targets") if module.params.get("wait"): status_achieved, registered_instances = wait_for_status( connection, module, target_group['TargetGroupArn'], instances_to_remove, 'unused') if not status_achieved: module.fail_json( msg='Error waiting for target deregistration - please check the AWS console') # register lambda target else: try: changed = False target = module.params.get("targets")[0] if len(current_targets["TargetHealthDescriptions"]) == 0: changed = True else: for item in current_targets["TargetHealthDescriptions"]: if target["Id"] != item["Target"]["Id"]: changed = True break # only one target is possible with lambda if changed: if target.get("Id"): response = connection.register_targets( TargetGroupArn=target_group['TargetGroupArn'], Targets=[ { "Id": target['Id'] } ], aws_retry=True ) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws( e, msg="Couldn't register targets") else: if target_type != "lambda": current_instances = current_targets['TargetHealthDescriptions'] if current_instances: instances_to_remove = [] for target in current_targets['TargetHealthDescriptions']: instances_to_remove.append({'Id': target['Target']['Id'], 'Port': target['Target']['Port']}) changed = True try: connection.deregister_targets(TargetGroupArn=target_group['TargetGroupArn'], Targets=instances_to_remove, aws_retry=True) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't remove targets") if module.params.get("wait"): status_achieved, registered_instances = wait_for_status( connection, module, target_group['TargetGroupArn'], instances_to_remove, 'unused') if not status_achieved: module.fail_json( msg='Error waiting for target deregistration - please check the AWS console') # remove lambda targets else: changed = False if current_targets["TargetHealthDescriptions"]: changed = True # only one target is possible with lambda target_to_remove = current_targets["TargetHealthDescriptions"][0]["Target"]["Id"] if changed: connection.deregister_targets( TargetGroupArn=target_group['TargetGroupArn'], Targets=[{"Id": target_to_remove}], aws_retry=True) else: try: connection.create_target_group(aws_retry=True, **params) changed = True new_target_group = True except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't create target group") target_group = get_target_group(connection, module, retry_missing=True) if module.params.get("targets"): if target_type != "lambda": params['Targets'] = module.params.get("targets") try: connection.register_targets(TargetGroupArn=target_group['TargetGroupArn'], Targets=params['Targets'], aws_retry=True) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't register targets") if module.params.get("wait"): status_achieved, registered_instances = wait_for_status(connection, module, target_group['TargetGroupArn'], params['Targets'], 'healthy') if not status_achieved: module.fail_json(msg='Error waiting for target registration to be healthy - please check the AWS console') else: try: target = module.params.get("targets")[0] response = connection.register_targets( TargetGroupArn=target_group['TargetGroupArn'], Targets=[ { "Id": target["Id"] } ], aws_retry=True ) changed = True except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws( e, msg="Couldn't register targets") attributes_update = create_or_update_attributes(connection, module, target_group, new_target_group) if attributes_update: changed = True # Tags - only need to play with tags if tags parameter has been set to something if tags is not None: # Get tags current_tags = get_target_group_tags(connection, module, target_group['TargetGroupArn']) # Delete necessary tags tags_need_modify, tags_to_delete = compare_aws_tags(boto3_tag_list_to_ansible_dict(current_tags), tags, purge_tags) if tags_to_delete: try: connection.remove_tags(ResourceArns=[target_group['TargetGroupArn']], TagKeys=tags_to_delete, aws_retry=True) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't delete tags from target group") changed = True # Add/update tags if tags_need_modify: try: connection.add_tags(ResourceArns=[target_group['TargetGroupArn']], Tags=ansible_dict_to_boto3_tag_list(tags_need_modify), aws_retry=True) except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't add tags to target group") changed = True # Get the target group again target_group = get_target_group(connection, module) # Get the target group attributes again target_group.update(get_tg_attributes(connection, module, target_group['TargetGroupArn'])) # Convert target_group to snake_case snaked_tg = camel_dict_to_snake_dict(target_group) snaked_tg['tags'] = boto3_tag_list_to_ansible_dict(get_target_group_tags(connection, module, target_group['TargetGroupArn'])) module.exit_json(changed=changed, **snaked_tg) def delete_target_group(connection, module): changed = False tg = get_target_group(connection, module) if tg: try: connection.delete_target_group(TargetGroupArn=tg['TargetGroupArn'], aws_retry=True) changed = True except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: module.fail_json_aws(e, msg="Couldn't delete target group") module.exit_json(changed=changed) def main(): protocols_list = ['http', 'https', 'tcp', 'tls', 'udp', 'tcp_udp', 'HTTP', 'HTTPS', 'TCP', 'TLS', 'UDP', 'TCP_UDP'] argument_spec = dict( deregistration_delay_timeout=dict(type='int'), deregistration_connection_termination=dict(type='bool', default=False), health_check_protocol=dict(choices=protocols_list), health_check_port=dict(), health_check_path=dict(), health_check_interval=dict(type='int'), health_check_timeout=dict(type='int'), healthy_threshold_count=dict(type='int'), modify_targets=dict(default=True, type='bool'), name=dict(required=True), port=dict(type='int'), protocol=dict(choices=protocols_list), protocol_version=dict(type='str', choices=['GRPC', 'HTTP1', 'HTTP2']), purge_tags=dict(default=True, type='bool'), stickiness_enabled=dict(type='bool'), stickiness_type=dict(), stickiness_lb_cookie_duration=dict(type='int'), stickiness_app_cookie_duration=dict(type='int'), stickiness_app_cookie_name=dict(), load_balancing_algorithm_type=dict(type='str', choices=['round_robin', 'least_outstanding_requests']), state=dict(required=True, choices=['present', 'absent']), successful_response_codes=dict(), tags=dict(type='dict', aliases=['resource_tags']), target_type=dict(choices=['instance', 'ip', 'lambda', 'alb']), targets=dict(type='list', elements='dict'), unhealthy_threshold_count=dict(type='int'), vpc_id=dict(), preserve_client_ip_enabled=dict(type='bool'), proxy_protocol_v2_enabled=dict(type='bool'), wait_timeout=dict(type='int', default=200), wait=dict(type='bool', default=False) ) required_by = dict( health_check_path=['health_check_protocol'], successful_response_codes=['health_check_protocol'], ) required_if = [ ['target_type', 'instance', ['protocol', 'port', 'vpc_id']], ['target_type', 'ip', ['protocol', 'port', 'vpc_id']], ['target_type', 'alb', ['protocol', 'port', 'vpc_id']], ] module = AnsibleAWSModule(argument_spec=argument_spec, required_by=required_by, required_if=required_if) if module.params.get('target_type') is None: module.params['target_type'] = 'instance' connection = module.client('elbv2', retry_decorator=AWSRetry.jittered_backoff(retries=10)) if module.params.get('state') == 'present': if module.params.get('protocol') in ['http', 'https', 'HTTP', 'HTTPS'] and module.params.get('deregistration_connection_termination', None): module.fail_json(msg="A target group with HTTP/S protocol does not support setting deregistration_connection_termination") create_or_update_target_group(connection, module) else: delete_target_group(connection, module) if __name__ == '__main__': main()