diff --git a/changelogs/fragments/migrate_autoscaling_group.yml b/changelogs/fragments/migrate_autoscaling_group.yml new file mode 100644 index 00000000000..5bd99301467 --- /dev/null +++ b/changelogs/fragments/migrate_autoscaling_group.yml @@ -0,0 +1,7 @@ +breaking_changes: +- autoscaling_group_info - The module has been migrated from the ``community.aws`` + collection. Playbooks using the Fully Qualified Collection Name for this module + should be updated to use ``amazon.aws.autoscaling_group_info``. +- autoscaling_group - The module has been migrated from the ``community.aws`` collection. + Playbooks using the Fully Qualified Collection Name for this module should be updated + to use ``amazon.aws.autoscaling_group``. diff --git a/meta/runtime.yml b/meta/runtime.yml index 581676660f4..71e8a3e3740 100644 --- a/meta/runtime.yml +++ b/meta/runtime.yml @@ -9,8 +9,6 @@ action_groups: - api_gateway_domain - application_autoscaling_policy - autoscaling_complete_lifecycle_action - - autoscaling_group_info - - autoscaling_group - autoscaling_instance_refresh_info - autoscaling_instance_refresh - autoscaling_launch_config_find @@ -252,6 +250,10 @@ action_groups: - wafv2_web_acl_info plugin_routing: modules: + autoscaling_group_info: + redirect: amazon.aws.autoscaling_group_info + autoscaling_group: + redirect: amazon.aws.autoscaling_group aws_acm: # Deprecation for this alias should not *start* prior to 2024-09-01 redirect: community.aws.acm_certificate @@ -397,10 +399,10 @@ plugin_routing: redirect: amazon.aws.cloudwatchevent_rule ec2_asg: # Deprecation for this alias should not *start* prior to 2024-09-01 - redirect: community.aws.autoscaling_group + redirect: amazon.aws.autoscaling_group ec2_asg_info: # Deprecation for this alias should not *start* prior to 2024-09-01 - redirect: community.aws.autoscaling_group_info + redirect: amazon.aws.autoscaling_group_info ec2_asg_instance_refresh: # Deprecation for this alias should not *start* prior to 2024-09-01 redirect: community.aws.autoscaling_instance_refresh diff --git a/plugins/modules/autoscaling_group.py b/plugins/modules/autoscaling_group.py deleted file mode 100644 index 84db04bce9c..00000000000 --- a/plugins/modules/autoscaling_group.py +++ /dev/null @@ -1,1950 +0,0 @@ -#!/usr/bin/python -# This file is part of Ansible -# 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: autoscaling_group -version_added: 1.0.0 -short_description: Create or delete AWS AutoScaling Groups (ASGs) -description: - - Can create or delete AWS AutoScaling Groups. - - Can be used with the M(community.aws.autoscaling_launch_config) module to manage Launch Configurations. - - Prior to release 5.0.0 this module was called C(community.aws.ec2_asg). - The usage did not change. -author: - - "Gareth Rushgrove (@garethr)" -options: - state: - description: - - Register or deregister the instance. - choices: ['present', 'absent'] - default: present - type: str - name: - description: - - Unique name for group to be created or deleted. - required: true - type: str - load_balancers: - description: - - List of ELB names to use for the group. Use for classic load balancers. - type: list - elements: str - target_group_arns: - description: - - List of target group ARNs to use for the group. Use for application load balancers. - type: list - elements: str - availability_zones: - description: - - List of availability zone names in which to create the group. - - Defaults to all the availability zones in the region if I(vpc_zone_identifier) is not set. - type: list - elements: str - launch_config_name: - description: - - Name of the Launch configuration to use for the group. See the community.aws.autoscaling_launch_config) module for managing these. - - If unspecified then the current group value will be used. One of I(launch_config_name) or I(launch_template) must be provided. - type: str - launch_template: - description: - - Dictionary describing the Launch Template to use - suboptions: - version: - description: - - The version number of the launch template to use. - - Defaults to latest version if not provided. - type: str - launch_template_name: - description: - - The name of the launch template. Only one of I(launch_template_name) or I(launch_template_id) is required. - type: str - launch_template_id: - description: - - The id of the launch template. Only one of I(launch_template_name) or I(launch_template_id) is required. - type: str - type: dict - min_size: - description: - - Minimum number of instances in group, if unspecified then the current group value will be used. - type: int - max_size: - description: - - Maximum number of instances in group, if unspecified then the current group value will be used. - type: int - max_instance_lifetime: - description: - - The maximum amount of time, in seconds, that an instance can be in service. - - Maximum instance lifetime must be equal to 0, between 604800 and 31536000 seconds (inclusive), or not specified. - - Value of 0 removes lifetime restriction. - type: int - mixed_instances_policy: - description: - - A mixed instance policy to use for the ASG. - - Only used when the ASG is configured to use a Launch Template (I(launch_template)). - - 'See also U(https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-autoscaling-autoscalinggroup-mixedinstancespolicy.html)' - required: false - suboptions: - instance_types: - description: - - A list of instance_types. - type: list - elements: str - required: false - instances_distribution: - description: - - >- - Specifies the distribution of On-Demand Instances and Spot Instances, the maximum price - to pay for Spot Instances, and how the Auto Scaling group allocates instance types - to fulfill On-Demand and Spot capacity. - - 'See also U(https://docs.aws.amazon.com/autoscaling/ec2/APIReference/API_InstancesDistribution.html)' - required: false - type: dict - version_added: 1.5.0 - suboptions: - on_demand_allocation_strategy: - description: - - Indicates how to allocate instance types to fulfill On-Demand capacity. - type: str - required: false - version_added: 1.5.0 - on_demand_base_capacity: - description: - - >- - The minimum amount of the Auto Scaling group's capacity that must be fulfilled by On-Demand - Instances. This base portion is provisioned first as your group scales. - - >- - Default if not set is 0. If you leave it set to 0, On-Demand Instances are launched as a - percentage of the Auto Scaling group's desired capacity, per the OnDemandPercentageAboveBaseCapacity setting. - type: int - required: false - version_added: 1.5.0 - on_demand_percentage_above_base_capacity: - description: - - Controls the percentages of On-Demand Instances and Spot Instances for your additional capacity beyond OnDemandBaseCapacity. - - Default if not set is 100. If you leave it set to 100, the percentages are 100% for On-Demand Instances and 0% for Spot Instances. - - 'Valid range: 0 to 100' - type: int - required: false - version_added: 1.5.0 - spot_allocation_strategy: - description: - - Indicates how to allocate instances across Spot Instance pools. - type: str - required: false - version_added: 1.5.0 - spot_instance_pools: - description: - - >- - The number of Spot Instance pools across which to allocate your Spot Instances. The Spot pools are determined from - the different instance types in the Overrides array of LaunchTemplate. Default if not set is 2. - - Used only when the Spot allocation strategy is lowest-price. - - 'Valid Range: Minimum value of 1. Maximum value of 20.' - type: int - required: false - version_added: 1.5.0 - spot_max_price: - description: - - The maximum price per unit hour that you are willing to pay for a Spot Instance. - - If you leave the value of this parameter blank (which is the default), the maximum Spot price is set at the On-Demand price. - - To remove a value that you previously set, include the parameter but leave the value blank. - type: str - required: false - version_added: 1.5.0 - type: dict - placement_group: - description: - - Physical location of your cluster placement group created in Amazon EC2. - type: str - desired_capacity: - description: - - Desired number of instances in group, if unspecified then the current group value will be used. - type: int - replace_all_instances: - description: - - In a rolling fashion, replace all instances that used the old launch configuration with one from the new launch configuration. - It increases the ASG size by I(replace_batch_size), waits for the new instances to be up and running. - After that, it terminates a batch of old instances, waits for the replacements, and repeats, until all old instances are replaced. - Once that's done the ASG size is reduced back to the expected size. - default: false - type: bool - replace_batch_size: - description: - - Number of instances you'd like to replace at a time. Used with I(replace_all_instances). - required: false - default: 1 - type: int - replace_instances: - description: - - List of I(instance_ids) belonging to the named AutoScalingGroup that you would like to terminate and be replaced with instances - matching the current launch configuration. - type: list - elements: str - detach_instances: - description: - - Removes one or more instances from the specified AutoScalingGroup. - - If I(decrement_desired_capacity) flag is not set, new instance(s) are launched to replace the detached instance(s). - - If a Classic Load Balancer is attached to the AutoScalingGroup, the instances are also deregistered from the load balancer. - - If there are target groups attached to the AutoScalingGroup, the instances are also deregistered from the target groups. - type: list - elements: str - version_added: 3.2.0 - decrement_desired_capacity: - description: - - Indicates whether the AutoScalingGroup decrements the desired capacity value by the number of instances detached. - default: false - type: bool - version_added: 3.2.0 - lc_check: - description: - - Check to make sure instances that are being replaced with I(replace_instances) do not already have the current I(launch_config). - default: true - type: bool - lt_check: - description: - - Check to make sure instances that are being replaced with I(replace_instances) do not already have the current - I(launch_template or I(launch_template) I(version). - default: true - type: bool - vpc_zone_identifier: - description: - - List of VPC subnets to use - type: list - elements: str - tags: - description: - - A list of tags to add to the Auto Scale Group. - - Optional key is I(propagate_at_launch), which defaults to true. - - When I(propagate_at_launch) is true the tags will be propagated to the Instances created. - type: list - elements: dict - purge_tags: - description: - - If C(true), existing tags will be purged from the resource to match exactly what is defined by I(tags) parameter. - - If the I(tags) parameter is not set then tags will not be modified. - default: false - type: bool - version_added: 3.2.0 - health_check_period: - description: - - Length of time in seconds after a new EC2 instance comes into service that Auto Scaling starts checking its health. - required: false - default: 300 - type: int - health_check_type: - description: - - The service you want the health status from, Amazon EC2 or Elastic Load Balancer. - required: false - default: EC2 - choices: ['EC2', 'ELB'] - type: str - default_cooldown: - description: - - The number of seconds after a scaling activity completes before another can begin. - default: 300 - type: int - wait_timeout: - description: - - How long to wait for instances to become viable when replaced. If you experience the error "Waited too long for ELB instances to be healthy", - try increasing this value. - default: 300 - type: int - wait_for_instances: - description: - - Wait for the ASG instances to be in a ready state before exiting. If instances are behind an ELB, it will wait until the ELB determines all - instances have a lifecycle_state of "InService" and a health_status of "Healthy". - default: true - type: bool - termination_policies: - description: - - An ordered list of criteria used for selecting instances to be removed from the Auto Scaling group when reducing capacity. - - Using I(termination_policies=Default) when modifying an existing AutoScalingGroup will result in the existing policy being retained - instead of changed to C(Default). - - 'Valid values include: C(Default), C(OldestInstance), C(NewestInstance), C(OldestLaunchConfiguration), C(ClosestToNextInstanceHour)' - - 'Full documentation of valid values can be found in the AWS documentation:' - - 'U(https://docs.aws.amazon.com/autoscaling/ec2/userguide/as-instance-termination.html#custom-termination-policy)' - default: Default - type: list - elements: str - notification_topic: - description: - - A SNS topic ARN to send auto scaling notifications to. - type: str - notification_types: - description: - - A list of auto scaling events to trigger notifications on. - default: - - 'autoscaling:EC2_INSTANCE_LAUNCH' - - 'autoscaling:EC2_INSTANCE_LAUNCH_ERROR' - - 'autoscaling:EC2_INSTANCE_TERMINATE' - - 'autoscaling:EC2_INSTANCE_TERMINATE_ERROR' - required: false - type: list - elements: str - suspend_processes: - description: - - A list of scaling processes to suspend. - - 'Valid values include:' - - C(Launch), C(Terminate), C(HealthCheck), C(ReplaceUnhealthy), C(AZRebalance), C(AlarmNotification), C(ScheduledActions), C(AddToLoadBalancer) - - 'Full documentation of valid values can be found in the AWS documentation:' - - 'U(https://docs.aws.amazon.com/autoscaling/ec2/userguide/as-suspend-resume-processes.html)' - default: [] - type: list - elements: str - metrics_collection: - description: - - Enable ASG metrics collection. - type: bool - default: false - metrics_granularity: - description: - - When I(metrics_collection=true) this will determine the granularity of metrics collected by CloudWatch. - default: "1Minute" - type: str - metrics_list: - description: - - List of autoscaling metrics to collect when I(metrics_collection=true). - default: - - 'GroupMinSize' - - 'GroupMaxSize' - - 'GroupDesiredCapacity' - - 'GroupInServiceInstances' - - 'GroupPendingInstances' - - 'GroupStandbyInstances' - - 'GroupTerminatingInstances' - - 'GroupTotalInstances' - type: list - elements: str -extends_documentation_fragment: - - amazon.aws.aws - - amazon.aws.ec2 -''' - -EXAMPLES = r''' -# Basic configuration with Launch Configuration - -- community.aws.autoscaling_group: - name: special - load_balancers: [ 'lb1', 'lb2' ] - availability_zones: [ 'eu-west-1a', 'eu-west-1b' ] - launch_config_name: 'lc-1' - min_size: 1 - max_size: 10 - desired_capacity: 5 - vpc_zone_identifier: [ 'subnet-abcd1234', 'subnet-1a2b3c4d' ] - tags: - - environment: production - propagate_at_launch: false - -# Rolling ASG Updates - -# Below is an example of how to assign a new launch config to an ASG and terminate old instances. -# -# All instances in "myasg" that do not have the launch configuration named "my_new_lc" will be terminated in -# a rolling fashion with instances using the current launch configuration, "my_new_lc". -# -# This could also be considered a rolling deploy of a pre-baked AMI. -# -# If this is a newly created group, the instances will not be replaced since all instances -# will have the current launch configuration. - -- name: create launch config - community.aws.autoscaling_launch_config: - name: my_new_lc - image_id: ami-lkajsf - key_name: mykey - region: us-east-1 - security_groups: sg-23423 - instance_type: m1.small - assign_public_ip: true - -- community.aws.autoscaling_group: - name: myasg - launch_config_name: my_new_lc - health_check_period: 60 - health_check_type: ELB - replace_all_instances: true - min_size: 5 - max_size: 5 - desired_capacity: 5 - region: us-east-1 - -# To only replace a couple of instances instead of all of them, supply a list -# to "replace_instances": - -- community.aws.autoscaling_group: - name: myasg - launch_config_name: my_new_lc - health_check_period: 60 - health_check_type: ELB - replace_instances: - - i-b345231 - - i-24c2931 - min_size: 5 - max_size: 5 - desired_capacity: 5 - region: us-east-1 - -# Basic Configuration with Launch Template - -- community.aws.autoscaling_group: - name: special - load_balancers: [ 'lb1', 'lb2' ] - availability_zones: [ 'eu-west-1a', 'eu-west-1b' ] - launch_template: - version: '1' - launch_template_name: 'lt-example' - launch_template_id: 'lt-123456' - min_size: 1 - max_size: 10 - desired_capacity: 5 - vpc_zone_identifier: [ 'subnet-abcd1234', 'subnet-1a2b3c4d' ] - tags: - - environment: production - propagate_at_launch: false - -# Basic Configuration with Launch Template using mixed instance policy - -- community.aws.autoscaling_group: - name: special - load_balancers: [ 'lb1', 'lb2' ] - availability_zones: [ 'eu-west-1a', 'eu-west-1b' ] - launch_template: - version: '1' - launch_template_name: 'lt-example' - launch_template_id: 'lt-123456' - mixed_instances_policy: - instance_types: - - t3a.large - - t3.large - - t2.large - instances_distribution: - on_demand_percentage_above_base_capacity: 0 - spot_allocation_strategy: capacity-optimized - min_size: 1 - max_size: 10 - desired_capacity: 5 - vpc_zone_identifier: [ 'subnet-abcd1234', 'subnet-1a2b3c4d' ] - tags: - - environment: production - propagate_at_launch: false -''' - -RETURN = r''' ---- -auto_scaling_group_name: - description: The unique name of the auto scaling group - returned: success - type: str - sample: "myasg" -auto_scaling_group_arn: - description: The unique ARN of the autoscaling group - returned: success - type: str - sample: "arn:aws:autoscaling:us-east-1:123456789012:autoScalingGroup:6a09ad6d-eeee-1234-b987-ee123ced01ad:autoScalingGroupName/myasg" -availability_zones: - description: The availability zones for the auto scaling group - returned: success - type: list - sample: [ - "us-east-1d" - ] -created_time: - description: Timestamp of create time of the auto scaling group - returned: success - type: str - sample: "2017-11-08T14:41:48.272000+00:00" -default_cooldown: - description: The default cooldown time in seconds. - returned: success - type: int - sample: 300 -desired_capacity: - description: The number of EC2 instances that should be running in this group. - returned: success - type: int - sample: 3 -healthcheck_period: - description: Length of time in seconds after a new EC2 instance comes into service that Auto Scaling starts checking its health. - returned: success - type: int - sample: 30 -healthcheck_type: - description: The service you want the health status from, one of "EC2" or "ELB". - returned: success - type: str - sample: "ELB" -healthy_instances: - description: Number of instances in a healthy state - returned: success - type: int - sample: 5 -in_service_instances: - description: Number of instances in service - returned: success - type: int - sample: 3 -instance_facts: - description: Dictionary of EC2 instances and their status as it relates to the ASG. - returned: success - type: dict - sample: { - "i-0123456789012": { - "health_status": "Healthy", - "launch_config_name": "public-webapp-production-1", - "lifecycle_state": "InService" - } - } -instances: - description: list of instance IDs in the ASG - returned: success - type: list - sample: [ - "i-0123456789012" - ] -launch_config_name: - description: > - Name of launch configuration associated with the ASG. Same as launch_configuration_name, - provided for compatibility with M(community.aws.autoscaling_group) module. - returned: success - type: str - sample: "public-webapp-production-1" -load_balancers: - description: List of load balancers names attached to the ASG. - returned: success - type: list - sample: ["elb-webapp-prod"] -max_instance_lifetime: - description: The maximum amount of time, in seconds, that an instance can be in service. - returned: success - type: int - sample: 604800 -max_size: - description: Maximum size of group - returned: success - type: int - sample: 3 -min_size: - description: Minimum size of group - returned: success - type: int - sample: 1 -mixed_instances_policy: - description: Returns the list of instance types if a mixed instances policy is set. - returned: success - type: list - sample: ["t3.micro", "t3a.micro"] -mixed_instances_policy_full: - description: Returns the full dictionary representation of the mixed instances policy if a mixed instances policy is set. - returned: success - type: dict - sample: { - "instances_distribution": { - "on_demand_allocation_strategy": "prioritized", - "on_demand_base_capacity": 0, - "on_demand_percentage_above_base_capacity": 0, - "spot_allocation_strategy": "capacity-optimized" - }, - "launch_template": { - "launch_template_specification": { - "launch_template_id": "lt-53c2425cffa544c23", - "launch_template_name": "random-LaunchTemplate", - "version": "2" - }, - "overrides": [ - { - "instance_type": "m5.xlarge" - }, - { - "instance_type": "m5a.xlarge" - }, - ] - } - } -pending_instances: - description: Number of instances in pending state - returned: success - type: int - sample: 1 -tags: - description: List of tags for the ASG, and whether or not each tag propagates to instances at launch. - returned: success - type: list - sample: [ - { - "key": "Name", - "value": "public-webapp-production-1", - "resource_id": "public-webapp-production-1", - "resource_type": "auto-scaling-group", - "propagate_at_launch": "true" - }, - { - "key": "env", - "value": "production", - "resource_id": "public-webapp-production-1", - "resource_type": "auto-scaling-group", - "propagate_at_launch": "true" - } - ] -target_group_arns: - description: List of ARNs of the target groups that the ASG populates - returned: success - type: list - sample: [ - "arn:aws:elasticloadbalancing:ap-southeast-2:123456789012:targetgroup/target-group-host-hello/1a2b3c4d5e6f1a2b", - "arn:aws:elasticloadbalancing:ap-southeast-2:123456789012:targetgroup/target-group-path-world/abcd1234abcd1234" - ] -target_group_names: - description: List of names of the target groups that the ASG populates - returned: success - type: list - sample: [ - "target-group-host-hello", - "target-group-path-world" - ] -termination_policies: - description: A list of termination policies for the group. - returned: success - type: list - sample: ["Default"] -unhealthy_instances: - description: Number of instances in an unhealthy state - returned: success - type: int - sample: 0 -viable_instances: - description: Number of instances in a viable state - returned: success - type: int - sample: 1 -vpc_zone_identifier: - description: VPC zone ID / subnet id for the auto scaling group - returned: success - type: str - sample: "subnet-a31ef45f" -metrics_collection: - description: List of enabled AutosSalingGroup metrics - returned: success - type: list - sample: [ - { - "Granularity": "1Minute", - "Metric": "GroupInServiceInstances" - } - ] -''' - -import time - -try: - import botocore -except ImportError: - pass # Handled by AnsibleAWSModule - -from ansible.module_utils._text import to_native - -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.core import scrub_none_parameters -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import snake_dict_to_camel_dict -from ansible_collections.amazon.aws.plugins.module_utils.ec2 import camel_dict_to_snake_dict - -ASG_ATTRIBUTES = ('AvailabilityZones', 'DefaultCooldown', 'DesiredCapacity', - 'HealthCheckGracePeriod', 'HealthCheckType', 'LaunchConfigurationName', - 'LoadBalancerNames', 'MaxInstanceLifetime', 'MaxSize', 'MinSize', - 'AutoScalingGroupName', 'PlacementGroup', 'TerminationPolicies', - 'VPCZoneIdentifier') - -INSTANCE_ATTRIBUTES = ('instance_id', 'health_status', 'lifecycle_state', 'launch_config_name') - -backoff_params = dict(retries=10, delay=3, backoff=1.5) - - -@AWSRetry.jittered_backoff(**backoff_params) -def describe_autoscaling_groups(connection, group_name): - pg = connection.get_paginator('describe_auto_scaling_groups') - return pg.paginate(AutoScalingGroupNames=[group_name]).build_full_result().get('AutoScalingGroups', []) - - -@AWSRetry.jittered_backoff(**backoff_params) -def deregister_lb_instances(connection, lb_name, instance_id): - connection.deregister_instances_from_load_balancer(LoadBalancerName=lb_name, Instances=[dict(InstanceId=instance_id)]) - - -@AWSRetry.jittered_backoff(**backoff_params) -def describe_instance_health(connection, lb_name, instances): - params = dict(LoadBalancerName=lb_name) - if instances: - params.update(Instances=instances) - return connection.describe_instance_health(**params) - - -@AWSRetry.jittered_backoff(**backoff_params) -def describe_target_health(connection, target_group_arn, instances): - return connection.describe_target_health(TargetGroupArn=target_group_arn, Targets=instances) - - -@AWSRetry.jittered_backoff(**backoff_params) -def suspend_asg_processes(connection, asg_name, processes): - connection.suspend_processes(AutoScalingGroupName=asg_name, ScalingProcesses=processes) - - -@AWSRetry.jittered_backoff(**backoff_params) -def resume_asg_processes(connection, asg_name, processes): - connection.resume_processes(AutoScalingGroupName=asg_name, ScalingProcesses=processes) - - -@AWSRetry.jittered_backoff(**backoff_params) -def describe_launch_configurations(connection, launch_config_name): - pg = connection.get_paginator('describe_launch_configurations') - return pg.paginate(LaunchConfigurationNames=[launch_config_name]).build_full_result() - - -@AWSRetry.jittered_backoff(**backoff_params) -def describe_launch_templates(connection, launch_template): - if launch_template['launch_template_id'] is not None: - try: - lt = connection.describe_launch_templates(LaunchTemplateIds=[launch_template['launch_template_id']]) - return lt - except is_boto3_error_code('InvalidLaunchTemplateName.NotFoundException'): - module.fail_json(msg="No launch template found matching: %s" % launch_template) - else: - try: - lt = connection.describe_launch_templates(LaunchTemplateNames=[launch_template['launch_template_name']]) - return lt - except is_boto3_error_code('InvalidLaunchTemplateName.NotFoundException'): - module.fail_json(msg="No launch template found matching: %s" % launch_template) - - -@AWSRetry.jittered_backoff(**backoff_params) -def create_asg(connection, **params): - connection.create_auto_scaling_group(**params) - - -@AWSRetry.jittered_backoff(**backoff_params) -def put_notification_config(connection, asg_name, topic_arn, notification_types): - connection.put_notification_configuration( - AutoScalingGroupName=asg_name, - TopicARN=topic_arn, - NotificationTypes=notification_types - ) - - -@AWSRetry.jittered_backoff(**backoff_params) -def del_notification_config(connection, asg_name, topic_arn): - connection.delete_notification_configuration( - AutoScalingGroupName=asg_name, - TopicARN=topic_arn - ) - - -@AWSRetry.jittered_backoff(**backoff_params) -def attach_load_balancers(connection, asg_name, load_balancers): - connection.attach_load_balancers(AutoScalingGroupName=asg_name, LoadBalancerNames=load_balancers) - - -@AWSRetry.jittered_backoff(**backoff_params) -def detach_load_balancers(connection, asg_name, load_balancers): - connection.detach_load_balancers(AutoScalingGroupName=asg_name, LoadBalancerNames=load_balancers) - - -@AWSRetry.jittered_backoff(**backoff_params) -def attach_lb_target_groups(connection, asg_name, target_group_arns): - connection.attach_load_balancer_target_groups(AutoScalingGroupName=asg_name, TargetGroupARNs=target_group_arns) - - -@AWSRetry.jittered_backoff(**backoff_params) -def detach_lb_target_groups(connection, asg_name, target_group_arns): - connection.detach_load_balancer_target_groups(AutoScalingGroupName=asg_name, TargetGroupARNs=target_group_arns) - - -@AWSRetry.jittered_backoff(**backoff_params) -def update_asg(connection, **params): - connection.update_auto_scaling_group(**params) - - -@AWSRetry.jittered_backoff(catch_extra_error_codes=['ScalingActivityInProgress'], **backoff_params) -def delete_asg(connection, asg_name, force_delete): - connection.delete_auto_scaling_group(AutoScalingGroupName=asg_name, ForceDelete=force_delete) - - -@AWSRetry.jittered_backoff(**backoff_params) -def terminate_asg_instance(connection, instance_id, decrement_capacity): - connection.terminate_instance_in_auto_scaling_group(InstanceId=instance_id, - ShouldDecrementDesiredCapacity=decrement_capacity) - - -@AWSRetry.jittered_backoff(**backoff_params) -def detach_asg_instances(connection, instance_ids, as_group_name, decrement_capacity): - connection.detach_instances(InstanceIds=instance_ids, AutoScalingGroupName=as_group_name, - ShouldDecrementDesiredCapacity=decrement_capacity) - - -def enforce_required_arguments_for_create(): - ''' As many arguments are not required for autoscale group deletion - they cannot be mandatory arguments for the module, so we enforce - them here ''' - missing_args = [] - if module.params.get('launch_config_name') is None and module.params.get('launch_template') is None: - module.fail_json(msg="Missing either launch_config_name or launch_template for autoscaling group create") - for arg in ('min_size', 'max_size'): - if module.params[arg] is None: - missing_args.append(arg) - if missing_args: - module.fail_json(msg="Missing required arguments for autoscaling group create: %s" % ",".join(missing_args)) - - -def get_properties(autoscaling_group): - properties = dict( - healthy_instances=0, - in_service_instances=0, - unhealthy_instances=0, - pending_instances=0, - viable_instances=0, - terminating_instances=0 - ) - instance_facts = dict() - autoscaling_group_instances = autoscaling_group.get('Instances') - - if autoscaling_group_instances: - properties['instances'] = [i['InstanceId'] for i in autoscaling_group_instances] - for i in autoscaling_group_instances: - instance_facts[i['InstanceId']] = { - 'health_status': i['HealthStatus'], - 'lifecycle_state': i['LifecycleState'] - } - if 'LaunchConfigurationName' in i: - instance_facts[i['InstanceId']]['launch_config_name'] = i['LaunchConfigurationName'] - elif 'LaunchTemplate' in i: - instance_facts[i['InstanceId']]['launch_template'] = i['LaunchTemplate'] - - if i['HealthStatus'] == 'Healthy' and i['LifecycleState'] == 'InService': - properties['viable_instances'] += 1 - - if i['HealthStatus'] == 'Healthy': - properties['healthy_instances'] += 1 - else: - properties['unhealthy_instances'] += 1 - - if i['LifecycleState'] == 'InService': - properties['in_service_instances'] += 1 - if i['LifecycleState'] == 'Terminating': - properties['terminating_instances'] += 1 - if i['LifecycleState'] == 'Pending': - properties['pending_instances'] += 1 - else: - properties['instances'] = [] - - properties['auto_scaling_group_name'] = autoscaling_group.get('AutoScalingGroupName') - properties['auto_scaling_group_arn'] = autoscaling_group.get('AutoScalingGroupARN') - properties['availability_zones'] = autoscaling_group.get('AvailabilityZones') - properties['created_time'] = autoscaling_group.get('CreatedTime') - properties['instance_facts'] = instance_facts - properties['load_balancers'] = autoscaling_group.get('LoadBalancerNames') - if 'LaunchConfigurationName' in autoscaling_group: - properties['launch_config_name'] = autoscaling_group.get('LaunchConfigurationName') - else: - properties['launch_template'] = autoscaling_group.get('LaunchTemplate') - properties['tags'] = autoscaling_group.get('Tags') - properties['max_instance_lifetime'] = autoscaling_group.get('MaxInstanceLifetime') - properties['min_size'] = autoscaling_group.get('MinSize') - properties['max_size'] = autoscaling_group.get('MaxSize') - properties['desired_capacity'] = autoscaling_group.get('DesiredCapacity') - properties['default_cooldown'] = autoscaling_group.get('DefaultCooldown') - properties['healthcheck_grace_period'] = autoscaling_group.get('HealthCheckGracePeriod') - properties['healthcheck_type'] = autoscaling_group.get('HealthCheckType') - properties['default_cooldown'] = autoscaling_group.get('DefaultCooldown') - properties['termination_policies'] = autoscaling_group.get('TerminationPolicies') - properties['target_group_arns'] = autoscaling_group.get('TargetGroupARNs') - properties['vpc_zone_identifier'] = autoscaling_group.get('VPCZoneIdentifier') - raw_mixed_instance_object = autoscaling_group.get('MixedInstancesPolicy') - if raw_mixed_instance_object: - properties['mixed_instances_policy_full'] = camel_dict_to_snake_dict(raw_mixed_instance_object) - properties['mixed_instances_policy'] = [x['InstanceType'] for x in raw_mixed_instance_object.get('LaunchTemplate').get('Overrides')] - - metrics = autoscaling_group.get('EnabledMetrics') - if metrics: - metrics.sort(key=lambda x: x["Metric"]) - properties['metrics_collection'] = metrics - - if properties['target_group_arns']: - elbv2_connection = module.client('elbv2') - tg_paginator = elbv2_connection.get_paginator('describe_target_groups') - tg_result = tg_paginator.paginate( - TargetGroupArns=properties['target_group_arns'] - ).build_full_result() - target_groups = tg_result['TargetGroups'] - else: - target_groups = [] - - properties['target_group_names'] = [ - tg['TargetGroupName'] - for tg in target_groups - ] - - return properties - - -def get_launch_object(connection, ec2_connection): - launch_object = dict() - launch_config_name = module.params.get('launch_config_name') - launch_template = module.params.get('launch_template') - mixed_instances_policy = module.params.get('mixed_instances_policy') - if launch_config_name is None and launch_template is None: - return launch_object - elif launch_config_name: - try: - launch_configs = describe_launch_configurations(connection, launch_config_name) - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Failed to describe launch configurations") - if len(launch_configs['LaunchConfigurations']) == 0: - module.fail_json(msg="No launch config found with name %s" % launch_config_name) - launch_object = {"LaunchConfigurationName": launch_configs['LaunchConfigurations'][0]['LaunchConfigurationName']} - return launch_object - elif launch_template: - lt = describe_launch_templates(ec2_connection, launch_template)['LaunchTemplates'][0] - if launch_template['version'] is not None: - launch_object = {"LaunchTemplate": {"LaunchTemplateId": lt['LaunchTemplateId'], "Version": launch_template['version']}} - else: - launch_object = {"LaunchTemplate": {"LaunchTemplateId": lt['LaunchTemplateId'], "Version": str(lt['LatestVersionNumber'])}} - - if mixed_instances_policy: - instance_types = mixed_instances_policy.get('instance_types', []) - instances_distribution = mixed_instances_policy.get('instances_distribution', {}) - policy = { - 'LaunchTemplate': { - 'LaunchTemplateSpecification': launch_object['LaunchTemplate'] - } - } - if instance_types: - policy['LaunchTemplate']['Overrides'] = [] - for instance_type in instance_types: - instance_type_dict = {'InstanceType': instance_type} - policy['LaunchTemplate']['Overrides'].append(instance_type_dict) - if instances_distribution: - instances_distribution_params = scrub_none_parameters(instances_distribution) - policy['InstancesDistribution'] = snake_dict_to_camel_dict(instances_distribution_params, capitalize_first=True) - launch_object['MixedInstancesPolicy'] = policy - return launch_object - - -def elb_dreg(asg_connection, group_name, instance_id): - as_group = describe_autoscaling_groups(asg_connection, group_name)[0] - wait_timeout = module.params.get('wait_timeout') - count = 1 - if as_group['LoadBalancerNames'] and as_group['HealthCheckType'] == 'ELB': - elb_connection = module.client('elb') - else: - return - - for lb in as_group['LoadBalancerNames']: - deregister_lb_instances(elb_connection, lb, instance_id) - module.debug("De-registering %s from ELB %s" % (instance_id, lb)) - - wait_timeout = time.time() + wait_timeout - while wait_timeout > time.time() and count > 0: - count = 0 - for lb in as_group['LoadBalancerNames']: - lb_instances = describe_instance_health(elb_connection, lb, []) - for i in lb_instances['InstanceStates']: - if i['InstanceId'] == instance_id and i['State'] == "InService": - count += 1 - module.debug("%s: %s, %s" % (i['InstanceId'], i['State'], i['Description'])) - time.sleep(10) - - if wait_timeout <= time.time(): - # waiting took too long - module.fail_json(msg="Waited too long for instance to deregister. {0}".format(time.asctime())) - - -def elb_healthy(asg_connection, elb_connection, group_name): - healthy_instances = set() - as_group = describe_autoscaling_groups(asg_connection, group_name)[0] - props = get_properties(as_group) - # get healthy, inservice instances from ASG - instances = [] - for instance, settings in props['instance_facts'].items(): - if settings['lifecycle_state'] == 'InService' and settings['health_status'] == 'Healthy': - instances.append(dict(InstanceId=instance)) - module.debug("ASG considers the following instances InService and Healthy: %s" % instances) - module.debug("ELB instance status:") - lb_instances = list() - for lb in as_group.get('LoadBalancerNames'): - # we catch a race condition that sometimes happens if the instance exists in the ASG - # but has not yet show up in the ELB - try: - lb_instances = describe_instance_health(elb_connection, lb, instances) - except is_boto3_error_code('InvalidInstance'): - return None - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except - module.fail_json_aws(e, msg="Failed to get load balancer.") - - for i in lb_instances.get('InstanceStates'): - if i['State'] == "InService": - healthy_instances.add(i['InstanceId']) - module.debug("ELB Health State %s: %s" % (i['InstanceId'], i['State'])) - return len(healthy_instances) - - -def tg_healthy(asg_connection, elbv2_connection, group_name): - healthy_instances = set() - as_group = describe_autoscaling_groups(asg_connection, group_name)[0] - props = get_properties(as_group) - # get healthy, inservice instances from ASG - instances = [] - for instance, settings in props['instance_facts'].items(): - if settings['lifecycle_state'] == 'InService' and settings['health_status'] == 'Healthy': - instances.append(dict(Id=instance)) - module.debug("ASG considers the following instances InService and Healthy: %s" % instances) - module.debug("Target Group instance status:") - tg_instances = list() - for tg in as_group.get('TargetGroupARNs'): - # we catch a race condition that sometimes happens if the instance exists in the ASG - # but has not yet show up in the ELB - try: - tg_instances = describe_target_health(elbv2_connection, tg, instances) - except is_boto3_error_code('InvalidInstance'): - return None - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except - module.fail_json_aws(e, msg="Failed to get target group.") - - for i in tg_instances.get('TargetHealthDescriptions'): - if i['TargetHealth']['State'] == "healthy": - healthy_instances.add(i['Target']['Id']) - module.debug("Target Group Health State %s: %s" % (i['Target']['Id'], i['TargetHealth']['State'])) - return len(healthy_instances) - - -def wait_for_elb(asg_connection, group_name): - wait_timeout = module.params.get('wait_timeout') - - # if the health_check_type is ELB, we want to query the ELBs directly for instance - # status as to avoid health_check_grace period that is awarded to ASG instances - as_group = describe_autoscaling_groups(asg_connection, group_name)[0] - - if as_group.get('LoadBalancerNames') and as_group.get('HealthCheckType') == 'ELB': - module.debug("Waiting for ELB to consider instances healthy.") - elb_connection = module.client('elb') - - wait_timeout = time.time() + wait_timeout - healthy_instances = elb_healthy(asg_connection, elb_connection, group_name) - - while healthy_instances < as_group.get('MinSize') and wait_timeout > time.time(): - healthy_instances = elb_healthy(asg_connection, elb_connection, group_name) - module.debug("ELB thinks %s instances are healthy." % healthy_instances) - time.sleep(10) - if wait_timeout <= time.time(): - # waiting took too long - module.fail_json(msg="Waited too long for ELB instances to be healthy. %s" % time.asctime()) - module.debug("Waiting complete. ELB thinks %s instances are healthy." % healthy_instances) - - -def wait_for_target_group(asg_connection, group_name): - wait_timeout = module.params.get('wait_timeout') - - # if the health_check_type is ELB, we want to query the ELBs directly for instance - # status as to avoid health_check_grace period that is awarded to ASG instances - as_group = describe_autoscaling_groups(asg_connection, group_name)[0] - - if as_group.get('TargetGroupARNs') and as_group.get('HealthCheckType') == 'ELB': - module.debug("Waiting for Target Group to consider instances healthy.") - elbv2_connection = module.client('elbv2') - - wait_timeout = time.time() + wait_timeout - healthy_instances = tg_healthy(asg_connection, elbv2_connection, group_name) - - while healthy_instances < as_group.get('MinSize') and wait_timeout > time.time(): - healthy_instances = tg_healthy(asg_connection, elbv2_connection, group_name) - module.debug("Target Group thinks %s instances are healthy." % healthy_instances) - time.sleep(10) - if wait_timeout <= time.time(): - # waiting took too long - module.fail_json(msg="Waited too long for ELB instances to be healthy. %s" % time.asctime()) - module.debug("Waiting complete. Target Group thinks %s instances are healthy." % healthy_instances) - - -def suspend_processes(ec2_connection, as_group): - suspend_processes = set(module.params.get('suspend_processes')) - - try: - suspended_processes = set([p['ProcessName'] for p in as_group['SuspendedProcesses']]) - except AttributeError: - # New ASG being created, no suspended_processes defined yet - suspended_processes = set() - - if suspend_processes == suspended_processes: - return False - - resume_processes = list(suspended_processes - suspend_processes) - if resume_processes: - resume_asg_processes(ec2_connection, module.params.get('name'), resume_processes) - - if suspend_processes: - suspend_asg_processes(ec2_connection, module.params.get('name'), list(suspend_processes)) - - return True - - -def create_autoscaling_group(connection): - group_name = module.params.get('name') - load_balancers = module.params['load_balancers'] - target_group_arns = module.params['target_group_arns'] - availability_zones = module.params['availability_zones'] - launch_config_name = module.params.get('launch_config_name') - launch_template = module.params.get('launch_template') - mixed_instances_policy = module.params.get('mixed_instances_policy') - min_size = module.params['min_size'] - max_size = module.params['max_size'] - max_instance_lifetime = module.params.get('max_instance_lifetime') - placement_group = module.params.get('placement_group') - desired_capacity = module.params.get('desired_capacity') - vpc_zone_identifier = module.params.get('vpc_zone_identifier') - set_tags = module.params.get('tags') - purge_tags = module.params.get('purge_tags') - health_check_period = module.params.get('health_check_period') - health_check_type = module.params.get('health_check_type') - default_cooldown = module.params.get('default_cooldown') - wait_for_instances = module.params.get('wait_for_instances') - wait_timeout = module.params.get('wait_timeout') - termination_policies = module.params.get('termination_policies') - notification_topic = module.params.get('notification_topic') - notification_types = module.params.get('notification_types') - metrics_collection = module.params.get('metrics_collection') - metrics_granularity = module.params.get('metrics_granularity') - metrics_list = module.params.get('metrics_list') - - try: - as_groups = describe_autoscaling_groups(connection, group_name) - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Failed to describe auto scaling groups.") - - ec2_connection = module.client('ec2') - - if vpc_zone_identifier: - vpc_zone_identifier = ','.join(vpc_zone_identifier) - - asg_tags = [] - for tag in set_tags: - for k, v in tag.items(): - if k != 'propagate_at_launch': - asg_tags.append(dict(Key=k, - Value=to_native(v), - PropagateAtLaunch=bool(tag.get('propagate_at_launch', True)), - ResourceType='auto-scaling-group', - ResourceId=group_name)) - if not as_groups: - if module.check_mode: - module.exit_json(changed=True, msg="Would have created AutoScalingGroup if not in check_mode.") - - if not vpc_zone_identifier and not availability_zones: - availability_zones = module.params['availability_zones'] = [zone['ZoneName'] for - zone in ec2_connection.describe_availability_zones()['AvailabilityZones']] - - enforce_required_arguments_for_create() - - if desired_capacity is None: - desired_capacity = min_size - ag = dict( - AutoScalingGroupName=group_name, - MinSize=min_size, - MaxSize=max_size, - DesiredCapacity=desired_capacity, - Tags=asg_tags, - HealthCheckGracePeriod=health_check_period, - HealthCheckType=health_check_type, - DefaultCooldown=default_cooldown, - TerminationPolicies=termination_policies) - if vpc_zone_identifier: - ag['VPCZoneIdentifier'] = vpc_zone_identifier - if availability_zones: - ag['AvailabilityZones'] = availability_zones - if placement_group: - ag['PlacementGroup'] = placement_group - if load_balancers: - ag['LoadBalancerNames'] = load_balancers - if target_group_arns: - ag['TargetGroupARNs'] = target_group_arns - if max_instance_lifetime: - ag['MaxInstanceLifetime'] = max_instance_lifetime - - launch_object = get_launch_object(connection, ec2_connection) - if 'LaunchConfigurationName' in launch_object: - ag['LaunchConfigurationName'] = launch_object['LaunchConfigurationName'] - elif 'LaunchTemplate' in launch_object: - if 'MixedInstancesPolicy' in launch_object: - ag['MixedInstancesPolicy'] = launch_object['MixedInstancesPolicy'] - else: - ag['LaunchTemplate'] = launch_object['LaunchTemplate'] - else: - module.fail_json_aws(e, msg="Missing LaunchConfigurationName or LaunchTemplate") - - try: - create_asg(connection, **ag) - if metrics_collection: - connection.enable_metrics_collection(AutoScalingGroupName=group_name, Granularity=metrics_granularity, Metrics=metrics_list) - - all_ag = describe_autoscaling_groups(connection, group_name) - if len(all_ag) == 0: - module.fail_json(msg="No auto scaling group found with the name %s" % group_name) - as_group = all_ag[0] - suspend_processes(connection, as_group) - if wait_for_instances: - wait_for_new_inst(connection, group_name, wait_timeout, desired_capacity, 'viable_instances') - if load_balancers: - wait_for_elb(connection, group_name) - # Wait for target group health if target group(s)defined - if target_group_arns: - wait_for_target_group(connection, group_name) - if notification_topic: - put_notification_config(connection, group_name, notification_topic, notification_types) - as_group = describe_autoscaling_groups(connection, group_name)[0] - asg_properties = get_properties(as_group) - changed = True - return changed, asg_properties - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Failed to create Autoscaling Group.") - else: - if module.check_mode: - module.exit_json(changed=True, msg="Would have modified AutoScalingGroup if required if not in check_mode.") - - as_group = as_groups[0] - initial_asg_properties = get_properties(as_group) - changed = False - - if suspend_processes(connection, as_group): - changed = True - - # process tag changes - have_tags = as_group.get('Tags') - want_tags = asg_tags - if purge_tags and not want_tags and have_tags: - connection.delete_tags(Tags=list(have_tags)) - - if len(set_tags) > 0: - if have_tags: - have_tags.sort(key=lambda x: x["Key"]) - if want_tags: - want_tags.sort(key=lambda x: x["Key"]) - dead_tags = [] - have_tag_keyvals = [x['Key'] for x in have_tags] - want_tag_keyvals = [x['Key'] for x in want_tags] - - for dead_tag in set(have_tag_keyvals).difference(want_tag_keyvals): - changed = True - if purge_tags: - dead_tags.append(dict( - ResourceId=as_group['AutoScalingGroupName'], ResourceType='auto-scaling-group', Key=dead_tag)) - have_tags = [have_tag for have_tag in have_tags if have_tag['Key'] != dead_tag] - - if dead_tags: - connection.delete_tags(Tags=dead_tags) - - zipped = zip(have_tags, want_tags) - if len(have_tags) != len(want_tags) or not all(x == y for x, y in zipped): - changed = True - connection.create_or_update_tags(Tags=asg_tags) - - # Handle load balancer attachments/detachments - # Attach load balancers if they are specified but none currently exist - if load_balancers and not as_group['LoadBalancerNames']: - changed = True - try: - attach_load_balancers(connection, group_name, load_balancers) - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Failed to update Autoscaling Group.") - - # Update load balancers if they are specified and one or more already exists - elif as_group['LoadBalancerNames']: - change_load_balancers = load_balancers is not None - # Get differences - if not load_balancers: - load_balancers = list() - wanted_elbs = set(load_balancers) - - has_elbs = set(as_group['LoadBalancerNames']) - # check if all requested are already existing - if has_elbs - wanted_elbs and change_load_balancers: - # if wanted contains less than existing, then we need to delete some - elbs_to_detach = has_elbs.difference(wanted_elbs) - if elbs_to_detach: - changed = True - try: - detach_load_balancers(connection, group_name, list(elbs_to_detach)) - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Failed to detach load balancers {0}".format(elbs_to_detach)) - if wanted_elbs - has_elbs: - # if has contains less than wanted, then we need to add some - elbs_to_attach = wanted_elbs.difference(has_elbs) - if elbs_to_attach: - changed = True - try: - attach_load_balancers(connection, group_name, list(elbs_to_attach)) - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Failed to attach load balancers {0}".format(elbs_to_attach)) - - # Handle target group attachments/detachments - # Attach target groups if they are specified but none currently exist - if target_group_arns and not as_group['TargetGroupARNs']: - changed = True - try: - attach_lb_target_groups(connection, group_name, target_group_arns) - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Failed to update Autoscaling Group.") - # Update target groups if they are specified and one or more already exists - elif target_group_arns is not None and as_group['TargetGroupARNs']: - # Get differences - wanted_tgs = set(target_group_arns) - has_tgs = set(as_group['TargetGroupARNs']) - - tgs_to_detach = has_tgs.difference(wanted_tgs) - if tgs_to_detach: - changed = True - try: - detach_lb_target_groups(connection, group_name, list(tgs_to_detach)) - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Failed to detach load balancer target groups {0}".format(tgs_to_detach)) - - tgs_to_attach = wanted_tgs.difference(has_tgs) - if tgs_to_attach: - changed = True - try: - attach_lb_target_groups(connection, group_name, list(tgs_to_attach)) - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json(msg="Failed to attach load balancer target groups {0}".format(tgs_to_attach)) - - # check for attributes that aren't required for updating an existing ASG - # check if min_size/max_size/desired capacity have been specified and if not use ASG values - if min_size is None: - min_size = as_group['MinSize'] - if max_size is None: - max_size = as_group['MaxSize'] - if desired_capacity is None: - desired_capacity = as_group['DesiredCapacity'] - ag = dict( - AutoScalingGroupName=group_name, - MinSize=min_size, - MaxSize=max_size, - DesiredCapacity=desired_capacity, - HealthCheckGracePeriod=health_check_period, - HealthCheckType=health_check_type, - DefaultCooldown=default_cooldown, - TerminationPolicies=termination_policies) - - # Get the launch object (config or template) if one is provided in args or use the existing one attached to ASG if not. - launch_object = get_launch_object(connection, ec2_connection) - if 'LaunchConfigurationName' in launch_object: - ag['LaunchConfigurationName'] = launch_object['LaunchConfigurationName'] - elif 'LaunchTemplate' in launch_object: - if 'MixedInstancesPolicy' in launch_object: - ag['MixedInstancesPolicy'] = launch_object['MixedInstancesPolicy'] - else: - ag['LaunchTemplate'] = launch_object['LaunchTemplate'] - else: - try: - ag['LaunchConfigurationName'] = as_group['LaunchConfigurationName'] - except Exception: - launch_template = as_group['LaunchTemplate'] - # Prefer LaunchTemplateId over Name as it's more specific. Only one can be used for update_asg. - ag['LaunchTemplate'] = {"LaunchTemplateId": launch_template['LaunchTemplateId'], "Version": launch_template['Version']} - - if availability_zones: - ag['AvailabilityZones'] = availability_zones - if vpc_zone_identifier: - ag['VPCZoneIdentifier'] = vpc_zone_identifier - if max_instance_lifetime is not None: - ag['MaxInstanceLifetime'] = max_instance_lifetime - - try: - update_asg(connection, **ag) - - if metrics_collection: - connection.enable_metrics_collection(AutoScalingGroupName=group_name, Granularity=metrics_granularity, Metrics=metrics_list) - else: - connection.disable_metrics_collection(AutoScalingGroupName=group_name, Metrics=metrics_list) - - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Failed to update autoscaling group") - - if notification_topic: - try: - put_notification_config(connection, group_name, notification_topic, notification_types) - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Failed to update Autoscaling Group notifications.") - if wait_for_instances: - wait_for_new_inst(connection, group_name, wait_timeout, desired_capacity, 'viable_instances') - # Wait for ELB health if ELB(s)defined - if load_balancers: - module.debug('\tWAITING FOR ELB HEALTH') - wait_for_elb(connection, group_name) - # Wait for target group health if target group(s)defined - - if target_group_arns: - module.debug('\tWAITING FOR TG HEALTH') - wait_for_target_group(connection, group_name) - - try: - as_group = describe_autoscaling_groups(connection, group_name)[0] - asg_properties = get_properties(as_group) - if asg_properties != initial_asg_properties: - changed = True - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Failed to read existing Autoscaling Groups.") - return changed, asg_properties - - -def delete_autoscaling_group(connection): - group_name = module.params.get('name') - notification_topic = module.params.get('notification_topic') - wait_for_instances = module.params.get('wait_for_instances') - wait_timeout = module.params.get('wait_timeout') - - if notification_topic: - del_notification_config(connection, group_name, notification_topic) - groups = describe_autoscaling_groups(connection, group_name) - if groups: - if module.check_mode: - module.exit_json(changed=True, msg="Would have deleted AutoScalingGroup if not in check_mode.") - wait_timeout = time.time() + wait_timeout - if not wait_for_instances: - delete_asg(connection, group_name, force_delete=True) - else: - updated_params = dict(AutoScalingGroupName=group_name, MinSize=0, MaxSize=0, DesiredCapacity=0) - update_asg(connection, **updated_params) - instances = True - while instances and wait_for_instances and wait_timeout >= time.time(): - tmp_groups = describe_autoscaling_groups(connection, group_name) - if tmp_groups: - tmp_group = tmp_groups[0] - if not tmp_group.get('Instances'): - instances = False - time.sleep(10) - - if wait_timeout <= time.time(): - # waiting took too long - module.fail_json(msg="Waited too long for old instances to terminate. %s" % time.asctime()) - - delete_asg(connection, group_name, force_delete=False) - while describe_autoscaling_groups(connection, group_name) and wait_timeout >= time.time(): - time.sleep(5) - if wait_timeout <= time.time(): - # waiting took too long - module.fail_json(msg="Waited too long for ASG to delete. %s" % time.asctime()) - return True - - return False - - -def get_chunks(l, n): - for i in range(0, len(l), n): - yield l[i:i + n] - - -def update_size(connection, group, max_size, min_size, dc): - module.debug("setting ASG sizes") - module.debug("minimum size: %s, desired_capacity: %s, max size: %s" % (min_size, dc, max_size)) - updated_group = dict() - updated_group['AutoScalingGroupName'] = group['AutoScalingGroupName'] - updated_group['MinSize'] = min_size - updated_group['MaxSize'] = max_size - updated_group['DesiredCapacity'] = dc - update_asg(connection, **updated_group) - - -def replace(connection): - batch_size = module.params.get('replace_batch_size') - wait_timeout = module.params.get('wait_timeout') - wait_for_instances = module.params.get('wait_for_instances') - group_name = module.params.get('name') - max_size = module.params.get('max_size') - min_size = module.params.get('min_size') - desired_capacity = module.params.get('desired_capacity') - launch_config_name = module.params.get('launch_config_name') - - # Required to maintain the default value being set to 'true' - if launch_config_name: - lc_check = module.params.get('lc_check') - else: - lc_check = False - # Mirror above behavior for Launch Templates - launch_template = module.params.get('launch_template') - if launch_template: - lt_check = module.params.get('lt_check') - else: - lt_check = False - replace_instances = module.params.get('replace_instances') - replace_all_instances = module.params.get('replace_all_instances') - - as_group = describe_autoscaling_groups(connection, group_name)[0] - if desired_capacity is None: - desired_capacity = as_group['DesiredCapacity'] - - if wait_for_instances: - wait_for_new_inst(connection, group_name, wait_timeout, as_group['MinSize'], 'viable_instances') - - props = get_properties(as_group) - instances = props['instances'] - if replace_all_instances: - # If replacing all instances, then set replace_instances to current set - # This allows replace_instances and replace_all_instances to behave same - replace_instances = instances - if replace_instances: - instances = replace_instances - - # check to see if instances are replaceable if checking launch configs - if launch_config_name: - new_instances, old_instances = get_instances_by_launch_config(props, lc_check, instances) - elif launch_template: - new_instances, old_instances = get_instances_by_launch_template(props, lt_check, instances) - - num_new_inst_needed = desired_capacity - len(new_instances) - - if lc_check or lt_check: - if num_new_inst_needed == 0 and old_instances: - module.debug("No new instances needed, but old instances are present. Removing old instances") - terminate_batch(connection, old_instances, instances, True) - as_group = describe_autoscaling_groups(connection, group_name)[0] - props = get_properties(as_group) - changed = True - return changed, props - - # we don't want to spin up extra instances if not necessary - if num_new_inst_needed < batch_size: - module.debug("Overriding batch size to %s" % num_new_inst_needed) - batch_size = num_new_inst_needed - - if not old_instances: - changed = False - return changed, props - - # check if min_size/max_size/desired capacity have been specified and if not use ASG values - if min_size is None: - min_size = as_group['MinSize'] - if max_size is None: - max_size = as_group['MaxSize'] - - # set temporary settings and wait for them to be reached - # This should get overwritten if the number of instances left is less than the batch size. - - as_group = describe_autoscaling_groups(connection, group_name)[0] - update_size(connection, as_group, max_size + batch_size, min_size + batch_size, desired_capacity + batch_size) - - if wait_for_instances: - wait_for_new_inst(connection, group_name, wait_timeout, as_group['MinSize'] + batch_size, 'viable_instances') - wait_for_elb(connection, group_name) - wait_for_target_group(connection, group_name) - - as_group = describe_autoscaling_groups(connection, group_name)[0] - props = get_properties(as_group) - instances = props['instances'] - if replace_instances: - instances = replace_instances - - module.debug("beginning main loop") - for i in get_chunks(instances, batch_size): - # break out of this loop if we have enough new instances - break_early, desired_size, term_instances = terminate_batch(connection, i, instances, False) - - if wait_for_instances: - wait_for_term_inst(connection, term_instances) - wait_for_new_inst(connection, group_name, wait_timeout, desired_size, 'viable_instances') - wait_for_elb(connection, group_name) - wait_for_target_group(connection, group_name) - - if break_early: - module.debug("breaking loop") - break - - update_size(connection, as_group, max_size, min_size, desired_capacity) - as_group = describe_autoscaling_groups(connection, group_name)[0] - asg_properties = get_properties(as_group) - module.debug("Rolling update complete.") - changed = True - return changed, asg_properties - - -def detach(connection): - group_name = module.params.get('name') - detach_instances = module.params.get('detach_instances') - as_group = describe_autoscaling_groups(connection, group_name)[0] - decrement_desired_capacity = module.params.get('decrement_desired_capacity') - min_size = module.params.get('min_size') - props = get_properties(as_group) - instances = props['instances'] - - # check if provided instance exists in asg, create list of instances to detach which exist in asg - instances_to_detach = [] - for instance_id in detach_instances: - if instance_id in instances: - instances_to_detach.append(instance_id) - - # check if setting decrement_desired_capacity will make desired_capacity smaller - # than the currently set minimum size in ASG configuration - if decrement_desired_capacity: - decremented_desired_capacity = len(instances) - len(instances_to_detach) - if min_size and min_size > decremented_desired_capacity: - module.fail_json( - msg="Detaching instance(s) with 'decrement_desired_capacity' flag set reduces number of instances to {0}\ - which is below current min_size {1}, please update AutoScalingGroup Sizes properly.".format(decremented_desired_capacity, min_size)) - - if instances_to_detach: - try: - detach_asg_instances(connection, instances_to_detach, group_name, decrement_desired_capacity) - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Failed to detach instances from AutoScaling Group") - - asg_properties = get_properties(as_group) - return True, asg_properties - - -def get_instances_by_launch_config(props, lc_check, initial_instances): - new_instances = [] - old_instances = [] - # old instances are those that have the old launch config - if lc_check: - for i in props['instances']: - # Check if migrating from launch_template to launch_config first - if 'launch_template' in props['instance_facts'][i]: - old_instances.append(i) - elif props['instance_facts'][i].get('launch_config_name') == props['launch_config_name']: - new_instances.append(i) - else: - old_instances.append(i) - - else: - module.debug("Comparing initial instances with current: %s" % initial_instances) - for i in props['instances']: - if i not in initial_instances: - new_instances.append(i) - else: - old_instances.append(i) - - module.debug("New instances: %s, %s" % (len(new_instances), new_instances)) - module.debug("Old instances: %s, %s" % (len(old_instances), old_instances)) - - return new_instances, old_instances - - -def get_instances_by_launch_template(props, lt_check, initial_instances): - new_instances = [] - old_instances = [] - # old instances are those that have the old launch template or version of the same launch template - if lt_check: - for i in props['instances']: - # Check if migrating from launch_config_name to launch_template_name first - if 'launch_config_name' in props['instance_facts'][i]: - old_instances.append(i) - elif props['instance_facts'][i].get('launch_template') == props['launch_template']: - new_instances.append(i) - else: - old_instances.append(i) - else: - module.debug("Comparing initial instances with current: %s" % initial_instances) - for i in props['instances']: - if i not in initial_instances: - new_instances.append(i) - else: - old_instances.append(i) - - module.debug("New instances: %s, %s" % (len(new_instances), new_instances)) - module.debug("Old instances: %s, %s" % (len(old_instances), old_instances)) - - return new_instances, old_instances - - -def list_purgeable_instances(props, lc_check, lt_check, replace_instances, initial_instances): - instances_to_terminate = [] - instances = (inst_id for inst_id in replace_instances if inst_id in props['instances']) - # check to make sure instances given are actually in the given ASG - # and they have a non-current launch config - if 'launch_config_name' in module.params: - if lc_check: - for i in instances: - if ( - 'launch_template' in props['instance_facts'][i] - or props['instance_facts'][i]['launch_config_name'] != props['launch_config_name'] - ): - instances_to_terminate.append(i) - else: - for i in instances: - if i in initial_instances: - instances_to_terminate.append(i) - elif 'launch_template' in module.params: - if lt_check: - for i in instances: - if ( - 'launch_config_name' in props['instance_facts'][i] - or props['instance_facts'][i]['launch_template'] != props['launch_template'] - ): - instances_to_terminate.append(i) - else: - for i in instances: - if i in initial_instances: - instances_to_terminate.append(i) - - return instances_to_terminate - - -def terminate_batch(connection, replace_instances, initial_instances, leftovers=False): - batch_size = module.params.get('replace_batch_size') - min_size = module.params.get('min_size') - desired_capacity = module.params.get('desired_capacity') - group_name = module.params.get('name') - lc_check = module.params.get('lc_check') - lt_check = module.params.get('lt_check') - decrement_capacity = False - break_loop = False - - as_group = describe_autoscaling_groups(connection, group_name)[0] - if desired_capacity is None: - desired_capacity = as_group['DesiredCapacity'] - - props = get_properties(as_group) - desired_size = as_group['MinSize'] - if module.params.get('launch_config_name'): - new_instances, old_instances = get_instances_by_launch_config(props, lc_check, initial_instances) - else: - new_instances, old_instances = get_instances_by_launch_template(props, lt_check, initial_instances) - num_new_inst_needed = desired_capacity - len(new_instances) - - # check to make sure instances given are actually in the given ASG - # and they have a non-current launch config - instances_to_terminate = list_purgeable_instances(props, lc_check, lt_check, replace_instances, initial_instances) - - module.debug("new instances needed: %s" % num_new_inst_needed) - module.debug("new instances: %s" % new_instances) - module.debug("old instances: %s" % old_instances) - module.debug("batch instances: %s" % ",".join(instances_to_terminate)) - - if num_new_inst_needed == 0: - decrement_capacity = True - if as_group['MinSize'] != min_size: - if min_size is None: - min_size = as_group['MinSize'] - updated_params = dict(AutoScalingGroupName=as_group['AutoScalingGroupName'], MinSize=min_size) - update_asg(connection, **updated_params) - module.debug("Updating minimum size back to original of %s" % min_size) - # if are some leftover old instances, but we are already at capacity with new ones - # we don't want to decrement capacity - if leftovers: - decrement_capacity = False - break_loop = True - instances_to_terminate = old_instances - desired_size = min_size - module.debug("No new instances needed") - - if num_new_inst_needed < batch_size and num_new_inst_needed != 0: - instances_to_terminate = instances_to_terminate[:num_new_inst_needed] - decrement_capacity = False - break_loop = False - module.debug("%s new instances needed" % num_new_inst_needed) - - module.debug("decrementing capacity: %s" % decrement_capacity) - - for instance_id in instances_to_terminate: - elb_dreg(connection, group_name, instance_id) - module.debug("terminating instance: %s" % instance_id) - terminate_asg_instance(connection, instance_id, decrement_capacity) - - # we wait to make sure the machines we marked as Unhealthy are - # no longer in the list - - return break_loop, desired_size, instances_to_terminate - - -def wait_for_term_inst(connection, term_instances): - wait_timeout = module.params.get('wait_timeout') - group_name = module.params.get('name') - as_group = describe_autoscaling_groups(connection, group_name)[0] - count = 1 - wait_timeout = time.time() + wait_timeout - while wait_timeout > time.time() and count > 0: - module.debug("waiting for instances to terminate") - count = 0 - as_group = describe_autoscaling_groups(connection, group_name)[0] - props = get_properties(as_group) - instance_facts = props['instance_facts'] - instances = (i for i in instance_facts if i in term_instances) - for i in instances: - lifecycle = instance_facts[i]['lifecycle_state'] - health = instance_facts[i]['health_status'] - module.debug("Instance %s has state of %s,%s" % (i, lifecycle, health)) - if lifecycle.startswith('Terminating') or health == 'Unhealthy': - count += 1 - time.sleep(10) - - if wait_timeout <= time.time(): - # waiting took too long - module.fail_json(msg="Waited too long for old instances to terminate. %s" % time.asctime()) - - -def wait_for_new_inst(connection, group_name, wait_timeout, desired_size, prop): - # make sure we have the latest stats after that last loop. - as_group = describe_autoscaling_groups(connection, group_name)[0] - props = get_properties(as_group) - module.debug("Waiting for %s = %s, currently %s" % (prop, desired_size, props[prop])) - # now we make sure that we have enough instances in a viable state - wait_timeout = time.time() + wait_timeout - while wait_timeout > time.time() and desired_size > props[prop]: - module.debug("Waiting for %s = %s, currently %s" % (prop, desired_size, props[prop])) - time.sleep(10) - as_group = describe_autoscaling_groups(connection, group_name)[0] - props = get_properties(as_group) - if wait_timeout <= time.time(): - # waiting took too long - module.fail_json(msg="Waited too long for new instances to become viable. %s" % time.asctime()) - module.debug("Reached %s: %s" % (prop, desired_size)) - return props - - -def asg_exists(connection): - group_name = module.params.get('name') - as_group = describe_autoscaling_groups(connection, group_name) - return bool(len(as_group)) - - -def main(): - argument_spec = dict( - name=dict(required=True, type='str'), - load_balancers=dict(type='list', elements='str'), - target_group_arns=dict(type='list', elements='str'), - availability_zones=dict(type='list', elements='str'), - launch_config_name=dict(type='str'), - launch_template=dict( - type='dict', - default=None, - options=dict( - version=dict(type='str'), - launch_template_name=dict(type='str'), - launch_template_id=dict(type='str'), - ) - ), - min_size=dict(type='int'), - max_size=dict(type='int'), - max_instance_lifetime=dict(type='int'), - mixed_instances_policy=dict( - type='dict', - default=None, - options=dict( - instance_types=dict( - type='list', - elements='str' - ), - instances_distribution=dict( - type='dict', - default=None, - options=dict( - on_demand_allocation_strategy=dict(type='str'), - on_demand_base_capacity=dict(type='int'), - on_demand_percentage_above_base_capacity=dict(type='int'), - spot_allocation_strategy=dict(type='str'), - spot_instance_pools=dict(type='int'), - spot_max_price=dict(type='str'), - ) - ) - ) - ), - placement_group=dict(type='str'), - desired_capacity=dict(type='int'), - vpc_zone_identifier=dict(type='list', elements='str'), - replace_batch_size=dict(type='int', default=1), - replace_all_instances=dict(type='bool', default=False), - replace_instances=dict(type='list', default=[], elements='str'), - detach_instances=dict(type='list', default=[], elements='str'), - decrement_desired_capacity=dict(type='bool', default=False), - lc_check=dict(type='bool', default=True), - lt_check=dict(type='bool', default=True), - wait_timeout=dict(type='int', default=300), - state=dict(default='present', choices=['present', 'absent']), - tags=dict(type='list', default=[], elements='dict'), - purge_tags=dict(type='bool', default=False), - health_check_period=dict(type='int', default=300), - health_check_type=dict(default='EC2', choices=['EC2', 'ELB']), - default_cooldown=dict(type='int', default=300), - wait_for_instances=dict(type='bool', default=True), - termination_policies=dict(type='list', default='Default', elements='str'), - notification_topic=dict(type='str', default=None), - notification_types=dict( - type='list', - default=[ - 'autoscaling:EC2_INSTANCE_LAUNCH', - 'autoscaling:EC2_INSTANCE_LAUNCH_ERROR', - 'autoscaling:EC2_INSTANCE_TERMINATE', - 'autoscaling:EC2_INSTANCE_TERMINATE_ERROR' - ], - elements='str' - ), - suspend_processes=dict(type='list', default=[], elements='str'), - metrics_collection=dict(type='bool', default=False), - metrics_granularity=dict(type='str', default='1Minute'), - metrics_list=dict( - type='list', - default=[ - 'GroupMinSize', - 'GroupMaxSize', - 'GroupDesiredCapacity', - 'GroupInServiceInstances', - 'GroupPendingInstances', - 'GroupStandbyInstances', - 'GroupTerminatingInstances', - 'GroupTotalInstances' - ], - elements='str' - ) - ) - - global module - module = AnsibleAWSModule( - argument_spec=argument_spec, - supports_check_mode=True, - mutually_exclusive=[ - ['replace_all_instances', 'replace_instances'], - ['replace_all_instances', 'detach_instances'], - ['launch_config_name', 'launch_template'], - ] - ) - - state = module.params.get('state') - replace_instances = module.params.get('replace_instances') - replace_all_instances = module.params.get('replace_all_instances') - detach_instances = module.params.get('detach_instances') - - connection = module.client('autoscaling') - changed = create_changed = replace_changed = detach_changed = False - exists = asg_exists(connection) - - if state == 'present': - create_changed, asg_properties = create_autoscaling_group(connection) - elif state == 'absent': - changed = delete_autoscaling_group(connection) - module.exit_json(changed=changed) - - # Only replace instances if asg existed at start of call - if ( - exists - and (replace_all_instances or replace_instances) - and (module.params.get('launch_config_name') or module.params.get('launch_template')) - ): - replace_changed, asg_properties = replace(connection) - - # Only detach instances if asg existed at start of call - if ( - exists - and (detach_instances) - and (module.params.get('launch_config_name') or module.params.get('launch_template')) - ): - detach_changed, asg_properties = detach(connection) - - if create_changed or replace_changed or detach_changed: - changed = True - - module.exit_json(changed=changed, **asg_properties) - - -if __name__ == '__main__': - main() diff --git a/plugins/modules/autoscaling_group_info.py b/plugins/modules/autoscaling_group_info.py deleted file mode 100644 index e8ec13a12ba..00000000000 --- a/plugins/modules/autoscaling_group_info.py +++ /dev/null @@ -1,458 +0,0 @@ -#!/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 = ''' ---- -module: autoscaling_group_info -version_added: 1.0.0 -short_description: Gather information about EC2 Auto Scaling Groups (ASGs) in AWS -description: - - Gather information about EC2 Auto Scaling Groups (ASGs) in AWS. - - Prior to release 5.0.0 this module was called C(community.aws.ec2_asg_info). - The usage did not change. -author: - - "Rob White (@wimnat)" -options: - name: - description: - - The prefix or name of the auto scaling group(s) you are searching for. - - "Note: This is a regular expression match with implicit '^' (beginning of string). Append '$' for a complete name match." - type: str - required: false - tags: - description: - - > - A dictionary/hash of tags in the format { tag1_name: 'tag1_value', tag2_name: 'tag2_value' } to match against the auto scaling - group(s) you are searching for. - required: false - type: dict -extends_documentation_fragment: - - amazon.aws.aws - - amazon.aws.ec2 -''' - -EXAMPLES = ''' -# Note: These examples do not set authentication details, see the AWS Guide for details. - -- name: Find all groups - community.aws.autoscaling_group_info: - register: asgs - -- name: Find a group with matching name/prefix - community.aws.autoscaling_group_info: - name: public-webserver-asg - register: asgs - -- name: Find a group with matching tags - community.aws.autoscaling_group_info: - tags: - project: webapp - env: production - register: asgs - -- name: Find a group with matching name/prefix and tags - community.aws.autoscaling_group_info: - name: myproject - tags: - env: production - register: asgs - -- name: Fail if no groups are found - community.aws.autoscaling_group_info: - name: public-webserver-asg - register: asgs - failed_when: "{{ asgs.results | length == 0 }}" - -- name: Fail if more than 1 group is found - community.aws.autoscaling_group_info: - name: public-webserver-asg - register: asgs - failed_when: "{{ asgs.results | length > 1 }}" -''' - -RETURN = ''' ---- -auto_scaling_group_arn: - description: The Amazon Resource Name of the ASG - returned: success - type: str - sample: "arn:aws:autoscaling:us-west-2:1234567890:autoScalingGroup:10787c52-0bcb-427d-82ba-c8e4b008ed2e:autoScalingGroupName/public-webapp-production-1" -auto_scaling_group_name: - description: Name of autoscaling group - returned: success - type: str - sample: "public-webapp-production-1" -availability_zones: - description: List of Availability Zones that are enabled for this ASG. - returned: success - type: list - sample: ["us-west-2a", "us-west-2b", "us-west-2a"] -created_time: - description: The date and time this ASG was created, in ISO 8601 format. - returned: success - type: str - sample: "2015-11-25T00:05:36.309Z" -default_cooldown: - description: The default cooldown time in seconds. - returned: success - type: int - sample: 300 -desired_capacity: - description: The number of EC2 instances that should be running in this group. - returned: success - type: int - sample: 3 -health_check_period: - description: Length of time in seconds after a new EC2 instance comes into service that Auto Scaling starts checking its health. - returned: success - type: int - sample: 30 -health_check_type: - description: The service you want the health status from, one of "EC2" or "ELB". - returned: success - type: str - sample: "ELB" -instances: - description: List of EC2 instances and their status as it relates to the ASG. - returned: success - type: list - sample: [ - { - "availability_zone": "us-west-2a", - "health_status": "Healthy", - "instance_id": "i-es22ad25", - "launch_configuration_name": "public-webapp-production-1", - "lifecycle_state": "InService", - "protected_from_scale_in": "false" - } - ] -launch_config_name: - description: > - Name of launch configuration associated with the ASG. Same as launch_configuration_name, - provided for compatibility with M(community.aws.autoscaling_group) module. - returned: success - type: str - sample: "public-webapp-production-1" -launch_configuration_name: - description: Name of launch configuration associated with the ASG. - returned: success - type: str - sample: "public-webapp-production-1" -lifecycle_hooks: - description: List of lifecycle hooks for the ASG. - returned: success - type: list - sample: [ - { - "AutoScalingGroupName": "public-webapp-production-1", - "DefaultResult": "ABANDON", - "GlobalTimeout": 172800, - "HeartbeatTimeout": 3600, - "LifecycleHookName": "instance-launch", - "LifecycleTransition": "autoscaling:EC2_INSTANCE_LAUNCHING" - }, - { - "AutoScalingGroupName": "public-webapp-production-1", - "DefaultResult": "ABANDON", - "GlobalTimeout": 172800, - "HeartbeatTimeout": 3600, - "LifecycleHookName": "instance-terminate", - "LifecycleTransition": "autoscaling:EC2_INSTANCE_TERMINATING" - } - ] -load_balancer_names: - description: List of load balancers names attached to the ASG. - returned: success - type: list - sample: ["elb-webapp-prod"] -max_size: - description: Maximum size of group - returned: success - type: int - sample: 3 -min_size: - description: Minimum size of group - returned: success - type: int - sample: 1 -new_instances_protected_from_scale_in: - description: Whether or not new instances a protected from automatic scaling in. - returned: success - type: bool - sample: "false" -placement_group: - description: Placement group into which instances are launched, if any. - returned: success - type: str - sample: None -status: - description: The current state of the group when DeleteAutoScalingGroup is in progress. - returned: success - type: str - sample: None -tags: - description: List of tags for the ASG, and whether or not each tag propagates to instances at launch. - returned: success - type: list - sample: [ - { - "key": "Name", - "value": "public-webapp-production-1", - "resource_id": "public-webapp-production-1", - "resource_type": "auto-scaling-group", - "propagate_at_launch": "true" - }, - { - "key": "env", - "value": "production", - "resource_id": "public-webapp-production-1", - "resource_type": "auto-scaling-group", - "propagate_at_launch": "true" - } - ] -target_group_arns: - description: List of ARNs of the target groups that the ASG populates - returned: success - type: list - sample: [ - "arn:aws:elasticloadbalancing:ap-southeast-2:123456789012:targetgroup/target-group-host-hello/1a2b3c4d5e6f1a2b", - "arn:aws:elasticloadbalancing:ap-southeast-2:123456789012:targetgroup/target-group-path-world/abcd1234abcd1234" - ] -target_group_names: - description: List of names of the target groups that the ASG populates - returned: success - type: list - sample: [ - "target-group-host-hello", - "target-group-path-world" - ] -termination_policies: - description: A list of termination policies for the group. - returned: success - type: str - sample: ["Default"] -''' - -import re - -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 - - -def match_asg_tags(tags_to_match, asg): - for key, value in tags_to_match.items(): - for tag in asg['Tags']: - if key == tag['Key'] and value == tag['Value']: - break - else: - return False - return True - - -def find_asgs(conn, module, name=None, tags=None): - """ - Args: - conn (boto3.AutoScaling.Client): Valid Boto3 ASG client. - name (str): Optional name of the ASG you are looking for. - tags (dict): Optional dictionary of tags and values to search for. - - Basic Usage: - >>> name = 'public-webapp-production' - >>> tags = { 'env': 'production' } - >>> conn = boto3.client('autoscaling', region_name='us-west-2') - >>> results = find_asgs(name, conn) - - Returns: - List - [ - { - "auto_scaling_group_arn": ( - "arn:aws:autoscaling:us-west-2:275977225706:autoScalingGroup:58abc686-9783-4528-b338-3ad6f1cbbbaf:" - "autoScalingGroupName/public-webapp-production" - ), - "auto_scaling_group_name": "public-webapp-production", - "availability_zones": ["us-west-2c", "us-west-2b", "us-west-2a"], - "created_time": "2016-02-02T23:28:42.481000+00:00", - "default_cooldown": 300, - "desired_capacity": 2, - "enabled_metrics": [], - "health_check_grace_period": 300, - "health_check_type": "ELB", - "instances": - [ - { - "availability_zone": "us-west-2c", - "health_status": "Healthy", - "instance_id": "i-047a12cb", - "launch_configuration_name": "public-webapp-production-1", - "lifecycle_state": "InService", - "protected_from_scale_in": false - }, - { - "availability_zone": "us-west-2a", - "health_status": "Healthy", - "instance_id": "i-7a29df2c", - "launch_configuration_name": "public-webapp-production-1", - "lifecycle_state": "InService", - "protected_from_scale_in": false - } - ], - "launch_config_name": "public-webapp-production-1", - "launch_configuration_name": "public-webapp-production-1", - "lifecycle_hooks": - [ - { - "AutoScalingGroupName": "public-webapp-production-1", - "DefaultResult": "ABANDON", - "GlobalTimeout": 172800, - "HeartbeatTimeout": 3600, - "LifecycleHookName": "instance-launch", - "LifecycleTransition": "autoscaling:EC2_INSTANCE_LAUNCHING" - }, - { - "AutoScalingGroupName": "public-webapp-production-1", - "DefaultResult": "ABANDON", - "GlobalTimeout": 172800, - "HeartbeatTimeout": 3600, - "LifecycleHookName": "instance-terminate", - "LifecycleTransition": "autoscaling:EC2_INSTANCE_TERMINATING" - } - ], - "load_balancer_names": ["public-webapp-production-lb"], - "max_size": 4, - "min_size": 2, - "new_instances_protected_from_scale_in": false, - "placement_group": None, - "status": None, - "suspended_processes": [], - "tags": - [ - { - "key": "Name", - "propagate_at_launch": true, - "resource_id": "public-webapp-production", - "resource_type": "auto-scaling-group", - "value": "public-webapp-production" - }, - { - "key": "env", - "propagate_at_launch": true, - "resource_id": "public-webapp-production", - "resource_type": "auto-scaling-group", - "value": "production" - } - ], - "target_group_names": [], - "target_group_arns": [], - "termination_policies": - [ - "Default" - ], - "vpc_zone_identifier": - [ - "subnet-a1b1c1d1", - "subnet-a2b2c2d2", - "subnet-a3b3c3d3" - ] - } - ] - """ - - try: - asgs_paginator = conn.get_paginator('describe_auto_scaling_groups') - asgs = asgs_paginator.paginate().build_full_result() - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg='Failed to describe AutoScalingGroups') - - if not asgs: - return asgs - - try: - elbv2 = module.client('elbv2') - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError): - # This is nice to have, not essential - elbv2 = None - matched_asgs = [] - - if name is not None: - # if the user didn't specify a name - name_prog = re.compile(r'^' + name) - - for asg in asgs['AutoScalingGroups']: - if name: - matched_name = name_prog.search(asg['AutoScalingGroupName']) - else: - matched_name = True - - if tags: - matched_tags = match_asg_tags(tags, asg) - else: - matched_tags = True - - if matched_name and matched_tags: - asg = camel_dict_to_snake_dict(asg) - # compatibility with autoscaling_group module - if 'launch_configuration_name' in asg: - asg['launch_config_name'] = asg['launch_configuration_name'] - # workaround for https://github.com/ansible/ansible/pull/25015 - if 'target_group_ar_ns' in asg: - asg['target_group_arns'] = asg['target_group_ar_ns'] - del asg['target_group_ar_ns'] - if asg.get('target_group_arns'): - if elbv2: - try: - tg_paginator = elbv2.get_paginator('describe_target_groups') - tg_result = tg_paginator.paginate(TargetGroupArns=asg['target_group_arns']).build_full_result() - asg['target_group_names'] = [tg['TargetGroupName'] for tg in tg_result['TargetGroups']] - except is_boto3_error_code('TargetGroupNotFound'): - asg['target_group_names'] = [] - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: # pylint: disable=duplicate-except - module.fail_json_aws(e, msg="Failed to describe Target Groups") - else: - asg['target_group_names'] = [] - # get asg lifecycle hooks if any - try: - asg_lifecyclehooks = conn.describe_lifecycle_hooks(AutoScalingGroupName=asg['auto_scaling_group_name']) - asg['lifecycle_hooks'] = asg_lifecyclehooks['LifecycleHooks'] - except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e: - module.fail_json_aws(e, msg="Failed to fetch information about ASG lifecycle hooks") - matched_asgs.append(asg) - - return matched_asgs - - -def main(): - - argument_spec = dict( - name=dict(type='str'), - tags=dict(type='dict'), - ) - - module = AnsibleAWSModule( - argument_spec=argument_spec, - supports_check_mode=True, - ) - - asg_name = module.params.get('name') - asg_tags = module.params.get('tags') - - autoscaling = module.client('autoscaling') - - results = find_asgs(autoscaling, module, name=asg_name, tags=asg_tags) - module.exit_json(results=results) - - -if __name__ == '__main__': - main() diff --git a/tests/integration/targets/autoscaling_group/aliases b/tests/integration/targets/autoscaling_group/aliases deleted file mode 100644 index 5619cbdc8d4..00000000000 --- a/tests/integration/targets/autoscaling_group/aliases +++ /dev/null @@ -1,7 +0,0 @@ -# reason: slow -# Tests take around 30 minutes - -slow -cloud/aws - -autoscaling_group_info diff --git a/tests/integration/targets/autoscaling_group/inventory b/tests/integration/targets/autoscaling_group/inventory deleted file mode 100644 index edc19ef5f3c..00000000000 --- a/tests/integration/targets/autoscaling_group/inventory +++ /dev/null @@ -1,8 +0,0 @@ -[tests] -create_update_delete -tag_operations -instance_detach - -[all:vars] -ansible_connection=local -ansible_python_interpreter="{{ ansible_playbook_python }}" diff --git a/tests/integration/targets/autoscaling_group/main.yml b/tests/integration/targets/autoscaling_group/main.yml deleted file mode 100644 index 3abe8e8a974..00000000000 --- a/tests/integration/targets/autoscaling_group/main.yml +++ /dev/null @@ -1,38 +0,0 @@ ---- -# Beware: most of our tests here are run in parallel. -# To add new tests you'll need to add a new host to the inventory and a matching -# '{{ inventory_hostname }}'.yml file in roles/ec2_asg/tasks/ - - -# Prepare the VPC and figure out which AMI to use -- hosts: all - gather_facts: no - tasks: - - module_defaults: - group/aws: - aws_access_key: "{{ aws_access_key }}" - aws_secret_key: "{{ aws_secret_key }}" - security_token: "{{ security_token | default(omit) }}" - region: "{{ aws_region }}" - block: - - include_role: - name: 'setup_ec2_facts' - - include_role: - name: 'ec2_asg' - tasks_from: env_setup.yml - rescue: - - include_role: - name: 'ec2_asg' - tasks_from: env_cleanup.yml - run_once: yes - - fail: - msg: 'Environment preparation failed' - run_once: yes - -# VPC should get cleaned up once all hosts have run -- hosts: all - gather_facts: no - strategy: free - serial: 6 - roles: - - ec2_asg diff --git a/tests/integration/targets/autoscaling_group/meta/main.yml b/tests/integration/targets/autoscaling_group/meta/main.yml deleted file mode 100644 index 1471b11f658..00000000000 --- a/tests/integration/targets/autoscaling_group/meta/main.yml +++ /dev/null @@ -1,2 +0,0 @@ -dependencies: - - setup_ec2_facts diff --git a/tests/integration/targets/autoscaling_group/roles/ec2_asg/defaults/main.yml b/tests/integration/targets/autoscaling_group/roles/ec2_asg/defaults/main.yml deleted file mode 100644 index d3a7707d8b8..00000000000 --- a/tests/integration/targets/autoscaling_group/roles/ec2_asg/defaults/main.yml +++ /dev/null @@ -1,3 +0,0 @@ ---- -load_balancer_name: "{{ tiny_prefix }}-lb" -ec2_asg_setup_run_once: true diff --git a/tests/integration/targets/autoscaling_group/roles/ec2_asg/tasks/create_update_delete.yml b/tests/integration/targets/autoscaling_group/roles/ec2_asg/tasks/create_update_delete.yml deleted file mode 100644 index 9345445ac4e..00000000000 --- a/tests/integration/targets/autoscaling_group/roles/ec2_asg/tasks/create_update_delete.yml +++ /dev/null @@ -1,645 +0,0 @@ ---- -# tasks file for test_ec2_asg - - # ============================================================ - -- name: Test create/update/delete AutoScalingGroups with ec2_asg - - block: - - # ============================================================ - - - name: test without specifying required module options - ec2_asg: - aws_access_key: "{{ aws_access_key }}" - aws_secret_key: "{{ aws_secret_key }}" - security_token: "{{ security_token | default(omit) }}" - ignore_errors: true - register: result - - - name: assert name is a required module option - assert: - that: - - "result.msg == 'missing required arguments: name'" - - - - name: ensure launch configs exist - ec2_lc: - name: "{{ item }}" - assign_public_ip: true - image_id: "{{ ec2_ami_id }}" - user_data: | - #cloud-config - package_upgrade: true - package_update: true - packages: - - httpd - runcmd: - - "service httpd start" - security_groups: "{{ sg.group_id }}" - instance_type: t3.micro - loop: - - "{{ resource_prefix }}-lc" - - "{{ resource_prefix }}-lc-2" - - # ============================================================ - - - name: launch asg and wait for instances to be deemed healthy (no ELB) - ec2_asg: - name: "{{ resource_prefix }}-asg" - launch_config_name: "{{ resource_prefix }}-lc" - desired_capacity: 1 - min_size: 1 - max_size: 1 - vpc_zone_identifier: "{{ testing_subnet.subnet.id }}" - state: present - wait_for_instances: yes - register: output - - - assert: - that: - - "output.viable_instances == 1" - - - name: Enable metrics collection - check_mode - ec2_asg: - name: "{{ resource_prefix }}-asg" - metrics_collection: yes - register: output - check_mode: true - - - assert: - that: - - output is changed - - output is not failed - - '"autoscaling:UpdateAutoScalingGroup" not in output.resource_actions' - - - name: Enable metrics collection - ec2_asg: - name: "{{ resource_prefix }}-asg" - metrics_collection: yes - register: output - - - assert: - that: - - output is changed - - - name: Enable metrics collection (idempotency) - ec2_asg: - name: "{{ resource_prefix }}-asg" - metrics_collection: yes - register: output - - - assert: - that: - - output is not changed - - - name: Disable metrics collection - check_mode - ec2_asg: - name: "{{ resource_prefix }}-asg" - metrics_collection: no - register: output - check_mode: true - - - assert: - that: - - output is changed - - output is not failed - - '"autoscaling:UpdateAutoScalingGroup" not in output.resource_actions' - - - - name: Disable metrics collection - ec2_asg: - name: "{{ resource_prefix }}-asg" - metrics_collection: no - register: output - - - assert: - that: - - output is changed - - - name: Disable metrics collection (idempotency) - ec2_asg: - name: "{{ resource_prefix }}-asg" - metrics_collection: no - register: output - - - assert: - that: - - output is not changed - - - name: kill asg - ec2_asg: - name: "{{ resource_prefix }}-asg" - state: absent - wait_timeout: 800 - async: 400 - - # ============================================================ - - - name: launch asg and do not wait for instances to be deemed healthy (no ELB) - ec2_asg: - name: "{{ resource_prefix }}-asg" - launch_config_name: "{{ resource_prefix }}-lc" - desired_capacity: 1 - min_size: 1 - max_size: 1 - vpc_zone_identifier: "{{ testing_subnet.subnet.id }}" - wait_for_instances: no - state: present - register: output - - - assert: - that: - - "output.viable_instances == 0" - - - name: kill asg - ec2_asg: - name: "{{ resource_prefix }}-asg" - state: absent - wait_timeout: 800 - register: output - retries: 3 - until: output is succeeded - delay: 10 - async: 400 - - # ============================================================ - - - name: create asg with asg metrics enabled - ec2_asg: - name: "{{ resource_prefix }}-asg" - metrics_collection: true - launch_config_name: "{{ resource_prefix }}-lc" - desired_capacity: 0 - min_size: 0 - max_size: 0 - vpc_zone_identifier: "{{ testing_subnet.subnet.id }}" - state: present - register: output - - - assert: - that: - - "'Group' in output.metrics_collection.0.Metric" - - - name: kill asg - ec2_asg: - name: "{{ resource_prefix }}-asg" - state: absent - wait_timeout: 800 - async: 400 - - # ============================================================ - - - name: launch load balancer - ec2_elb_lb: - name: "{{ load_balancer_name }}" - state: present - security_group_ids: - - "{{ sg.group_id }}" - subnets: "{{ testing_subnet.subnet.id }}" - connection_draining_timeout: 60 - listeners: - - protocol: http - load_balancer_port: 80 - instance_port: 80 - health_check: - ping_protocol: tcp - ping_port: 80 - ping_path: "/" - response_timeout: 5 - interval: 10 - unhealthy_threshold: 4 - healthy_threshold: 2 - register: load_balancer - - - name: launch asg and wait for instances to be deemed healthy (ELB) - ec2_asg: - name: "{{ resource_prefix }}-asg" - launch_config_name: "{{ resource_prefix }}-lc" - health_check_type: ELB - desired_capacity: 1 - min_size: 1 - max_size: 1 - health_check_period: 300 - vpc_zone_identifier: "{{ testing_subnet.subnet.id }}" - load_balancers: "{{ load_balancer_name }}" - wait_for_instances: yes - wait_timeout: 900 - state: present - register: output - - - assert: - that: - - "output.viable_instances == 1" - - # ============================================================ - - # grow scaling group to 3 - - name: add 2 more instances wait for instances to be deemed healthy (ELB) - ec2_asg: - name: "{{ resource_prefix }}-asg" - launch_config_name: "{{ resource_prefix }}-lc" - health_check_type: ELB - desired_capacity: 3 - min_size: 3 - max_size: 5 - health_check_period: 600 - vpc_zone_identifier: "{{ testing_subnet.subnet.id }}" - load_balancers: "{{ load_balancer_name }}" - wait_for_instances: yes - wait_timeout: 1200 - state: present - register: output - - - assert: - that: - - "output.viable_instances == 3" - - # ============================================================ - - # Test max_instance_lifetime option - - name: enable asg max_instance_lifetime - ec2_asg: - name: "{{ resource_prefix }}-asg" - max_instance_lifetime: 604801 - register: output - - - name: ensure max_instance_lifetime is set - assert: - that: - - output.max_instance_lifetime == 604801 - - - name: run without max_instance_lifetime - ec2_asg: - name: "{{ resource_prefix }}-asg" - launch_config_name: "{{ resource_prefix }}-lc" - - - name: ensure max_instance_lifetime not affected by defaults - assert: - that: - - output.max_instance_lifetime == 604801 - - - name: disable asg max_instance_lifetime - ec2_asg: - name: "{{ resource_prefix }}-asg" - launch_config_name: "{{ resource_prefix }}-lc" - max_instance_lifetime: 0 - register: output - - - name: ensure max_instance_lifetime is not set - assert: - that: - - not output.max_instance_lifetime - - # ============================================================ - - # perform rolling replace with different launch configuration - - name: perform rolling update to new AMI - ec2_asg: - name: "{{ resource_prefix }}-asg" - launch_config_name: "{{ resource_prefix }}-lc-2" - health_check_type: ELB - desired_capacity: 3 - min_size: 1 - max_size: 5 - health_check_period: 900 - load_balancers: "{{ load_balancer_name }}" - vpc_zone_identifier: "{{ testing_subnet.subnet.id }}" - wait_for_instances: yes - replace_all_instances: yes - wait_timeout: 1800 - state: present - register: output - - # ensure that all instances have new launch config - - assert: - that: - - "item.value.launch_config_name == '{{ resource_prefix }}-lc-2'" - loop: "{{ output.instance_facts | dict2items }}" - - # assert they are all healthy and that the rolling update resulted in the appropriate number of instances - - assert: - that: - - "output.viable_instances == 3" - - # ============================================================ - - # perform rolling replace with the original launch configuration - - name: perform rolling update to new AMI while removing the load balancer - ec2_asg: - name: "{{ resource_prefix }}-asg" - launch_config_name: "{{ resource_prefix }}-lc" - health_check_type: EC2 - desired_capacity: 3 - min_size: 1 - max_size: 5 - health_check_period: 900 - load_balancers: [] - vpc_zone_identifier: "{{ testing_subnet.subnet.id }}" - wait_for_instances: yes - replace_all_instances: yes - wait_timeout: 1800 - state: present - register: output - - # ensure that all instances have new launch config - - assert: - that: - - "item.value.launch_config_name == '{{ resource_prefix }}-lc'" - loop: "{{ output.instance_facts | dict2items }}" - - # assert they are all healthy and that the rolling update resulted in the appropriate number of instances - # there should be the same number of instances as there were before the rolling update was performed - - assert: - that: - - "output.viable_instances == 3" - - # ============================================================ - - # perform rolling replace with new launch configuration and lc_check:false - - name: "perform rolling update to new AMI with lc_check: false" - ec2_asg: - name: "{{ resource_prefix }}-asg" - launch_config_name: "{{ resource_prefix }}-lc-2" - health_check_type: EC2 - desired_capacity: 3 - min_size: 1 - max_size: 5 - health_check_period: 900 - load_balancers: [] - vpc_zone_identifier: "{{ testing_subnet.subnet.id }}" - wait_for_instances: yes - replace_all_instances: yes - replace_batch_size: 3 - lc_check: false - wait_timeout: 1800 - state: present - - # Collect ec2_asg_info - - name: get ec2_asg info - ec2_asg_info: - name: "{{ resource_prefix }}-asg" - register: output - - # Since we started with 3 instances and replace all of them. - # We should see only 3 instances total. - - assert: - that: - - output.results[0].instances | length == 3 - - # ============================================================ - - - name: kill asg - ec2_asg: - name: "{{ resource_prefix }}-asg" - state: absent - wait_timeout: 800 - async: 400 - - # Create new asg with replace_all_instances and lc_check:false - - name: "new asg with lc_check: false" - ec2_asg: - name: "{{ resource_prefix }}-asg" - launch_config_name: "{{ resource_prefix }}-lc" - health_check_type: EC2 - desired_capacity: 3 - min_size: 1 - max_size: 5 - health_check_period: 900 - load_balancers: [] - vpc_zone_identifier: "{{ testing_subnet.subnet.id }}" - wait_for_instances: yes - replace_all_instances: yes - replace_batch_size: 3 - lc_check: false - wait_timeout: 1800 - state: present - - # Collect ec2_asg_info - - name: get ec2_asg information - ec2_asg_info: - name: "{{ resource_prefix }}-asg" - register: output - - # Get all instance_ids we saw and assert we saw number expected - # Should only see 3 (don't replace instances we just created) - - assert: - that: - - output.results[0].instances | length == 3 - - # we need a launch template, otherwise we cannot test the mixed instance policy - - name: create launch template for autoscaling group to test its mixed instances policy - ec2_launch_template: - template_name: "{{ resource_prefix }}-lt" - image_id: "{{ ec2_ami_id }}" - instance_type: t3.micro - credit_specification: - cpu_credits: standard - network_interfaces: - - associate_public_ip_address: yes - delete_on_termination: yes - device_index: 0 - groups: - - "{{ sg.group_id }}" - - - name: update autoscaling group with mixed-instances policy with mixed instances types - check_mode - ec2_asg: - name: "{{ resource_prefix }}-asg" - launch_template: - launch_template_name: "{{ resource_prefix }}-lt" - desired_capacity: 1 - min_size: 1 - max_size: 1 - vpc_zone_identifier: "{{ testing_subnet.subnet.id }}" - state: present - mixed_instances_policy: - instance_types: - - t3.micro - - t2.nano - wait_for_instances: yes - register: output - check_mode: true - - - assert: - that: - - output is changed - - output is not failed - - '"autoscaling:CreateOrUpdateTags" not in output.resource_actions' - - - name: update autoscaling group with mixed-instances policy with mixed instances types - ec2_asg: - name: "{{ resource_prefix }}-asg" - launch_template: - launch_template_name: "{{ resource_prefix }}-lt" - desired_capacity: 1 - min_size: 1 - max_size: 1 - vpc_zone_identifier: "{{ testing_subnet.subnet.id }}" - state: present - mixed_instances_policy: - instance_types: - - t3.micro - - t2.nano - wait_for_instances: yes - register: output - - - assert: - that: - - "output.mixed_instances_policy | length == 2" - - "output.mixed_instances_policy[0] == 't3.micro'" - - "output.mixed_instances_policy[1] == 't2.nano'" - - - name: update autoscaling group with mixed-instances policy with instances_distribution - ec2_asg: - name: "{{ resource_prefix }}-asg" - launch_template: - launch_template_name: "{{ resource_prefix }}-lt" - desired_capacity: 1 - min_size: 1 - max_size: 1 - vpc_zone_identifier: "{{ testing_subnet.subnet.id }}" - state: present - mixed_instances_policy: - instance_types: - - t3.micro - - t2.nano - instances_distribution: - on_demand_percentage_above_base_capacity: 0 - spot_allocation_strategy: capacity-optimized - wait_for_instances: yes - register: output - - - assert: - that: - - "output.mixed_instances_policy_full['launch_template']['overrides'][0]['instance_type'] == 't3.micro'" - - "output.mixed_instances_policy_full['launch_template']['overrides'][1]['instance_type'] == 't2.nano'" - - "output.mixed_instances_policy_full['instances_distribution']['on_demand_percentage_above_base_capacity'] == 0" - - "output.mixed_instances_policy_full['instances_distribution']['spot_allocation_strategy'] == 'capacity-optimized'" - - # ============================================================ - - # Target group names have max length of 32 characters - - set_fact: - tg1_name: "{{ (resource_prefix + '-tg1' ) | regex_search('(.{1,32})$') }}" - tg2_name: "{{ (resource_prefix + '-tg2' ) | regex_search('(.{1,32})$') }}" - - - name: create target group 1 - elb_target_group: - name: "{{ tg1_name }}" - protocol: tcp - port: 80 - health_check_protocol: tcp - health_check_port: 80 - healthy_threshold_count: 2 - unhealthy_threshold_count: 2 - vpc_id: "{{ testing_vpc.vpc.id }}" - state: present - register: out_tg1 - - - name: create target group 2 - elb_target_group: - name: "{{ tg2_name }}" - protocol: tcp - port: 80 - health_check_protocol: tcp - health_check_port: 80 - healthy_threshold_count: 2 - unhealthy_threshold_count: 2 - vpc_id: "{{ testing_vpc.vpc.id }}" - state: present - register: out_tg2 - - - name: update autoscaling group with tg1 - ec2_asg: - name: "{{ resource_prefix }}-asg" - launch_template: - launch_template_name: "{{ resource_prefix }}-lt" - target_group_arns: - - "{{ out_tg1.target_group_arn }}" - desired_capacity: 1 - min_size: 1 - max_size: 1 - state: present - wait_for_instances: yes - register: output - - - assert: - that: - - output.target_group_arns[0] == out_tg1.target_group_arn - - - name: update autoscaling group add tg2 - ec2_asg: - name: "{{ resource_prefix }}-asg" - launch_template: - launch_template_name: "{{ resource_prefix }}-lt" - target_group_arns: - - "{{ out_tg1.target_group_arn }}" - - "{{ out_tg2.target_group_arn }}" - desired_capacity: 1 - min_size: 1 - max_size: 1 - state: present - wait_for_instances: yes - register: output - - - assert: - that: - - "output.target_group_arns | length == 2" - - - name: update autoscaling group remove tg1 - ec2_asg: - name: "{{ resource_prefix }}-asg" - launch_template: - launch_template_name: "{{ resource_prefix }}-lt" - target_group_arns: - - "{{ out_tg2.target_group_arn }}" - desired_capacity: 1 - min_size: 1 - max_size: 1 - state: present - wait_for_instances: yes - register: output - - - assert: - that: - - "output.target_group_arns | length == 1" - - "output.target_group_arns[0] == out_tg2.target_group_arn" - - - name: update autoscaling group remove tg2 and add tg1 - ec2_asg: - name: "{{ resource_prefix }}-asg" - launch_template: - launch_template_name: "{{ resource_prefix }}-lt" - target_group_arns: - - "{{ out_tg1.target_group_arn }}" - desired_capacity: 1 - min_size: 1 - max_size: 1 - state: present - wait_for_instances: yes - register: output - - - assert: - that: - - "output.target_group_arns | length == 1" - - "output.target_group_arns[0] == out_tg1.target_group_arn" - - - name: target group no change - ec2_asg: - name: "{{ resource_prefix }}-asg" - launch_template: - launch_template_name: "{{ resource_prefix }}-lt" - target_group_arns: - - "{{ out_tg1.target_group_arn }}" - desired_capacity: 1 - min_size: 1 - max_size: 1 - state: present - wait_for_instances: yes - register: output - - - assert: - that: - - "output.target_group_arns | length == 1" - - "output.target_group_arns[0] == out_tg1.target_group_arn" - - "output.changed == false" diff --git a/tests/integration/targets/autoscaling_group/roles/ec2_asg/tasks/env_cleanup.yml b/tests/integration/targets/autoscaling_group/roles/ec2_asg/tasks/env_cleanup.yml deleted file mode 100644 index 0f1cc825db0..00000000000 --- a/tests/integration/targets/autoscaling_group/roles/ec2_asg/tasks/env_cleanup.yml +++ /dev/null @@ -1,124 +0,0 @@ -- name: kill asg - ec2_asg: - name: "{{ resource_prefix }}-asg" - state: absent - register: removed - until: removed is not failed - ignore_errors: true - retries: 10 - -# Remove the testing dependencies -- name: remove target group - elb_target_group: - name: "{{ item }}" - state: absent - register: removed - until: removed is not failed - ignore_errors: true - retries: 10 - loop: - - "{{ tg1_name }}" - - "{{ tg2_name }}" - -- name: remove the load balancer - ec2_elb_lb: - name: "{{ load_balancer_name }}" - state: absent - security_group_ids: - - "{{ sg.group_id }}" - subnets: "{{ testing_subnet.subnet.id }}" - wait: true - connection_draining_timeout: 60 - listeners: - - protocol: http - load_balancer_port: 80 - instance_port: 80 - health_check: - ping_protocol: tcp - ping_port: 80 - ping_path: "/" - response_timeout: 5 - interval: 10 - unhealthy_threshold: 4 - healthy_threshold: 2 - register: removed - until: removed is not failed - ignore_errors: true - retries: 10 - -- name: remove launch configs - ec2_lc: - name: "{{ item }}" - state: absent - register: removed - until: removed is not failed - ignore_errors: true - retries: 10 - loop: - - "{{ resource_prefix }}-lc" - - "{{ resource_prefix }}-lc-2" - -- name: delete launch template - ec2_launch_template: - name: "{{ resource_prefix }}-lt" - state: absent - register: del_lt - retries: 10 - until: del_lt is not failed - ignore_errors: true - -- name: remove the security group - ec2_group: - name: "{{ resource_prefix }}-sg" - description: a security group for ansible tests - vpc_id: "{{ testing_vpc.vpc.id }}" - state: absent - register: removed - until: removed is not failed - ignore_errors: true - retries: 10 - -- name: remove routing rules - ec2_vpc_route_table: - state: absent - vpc_id: "{{ testing_vpc.vpc.id }}" - tags: - created: "{{ resource_prefix }}-route" - routes: - - dest: 0.0.0.0/0 - gateway_id: "{{ igw.gateway_id }}" - subnets: - - "{{ testing_subnet.subnet.id }}" - register: removed - until: removed is not failed - ignore_errors: true - retries: 10 - -- name: remove internet gateway - ec2_vpc_igw: - vpc_id: "{{ testing_vpc.vpc.id }}" - state: absent - register: removed - until: removed is not failed - ignore_errors: true - retries: 10 - -- name: remove the subnet - ec2_vpc_subnet: - state: absent - vpc_id: "{{ testing_vpc.vpc.id }}" - cidr: 10.55.77.0/24 - register: removed - until: removed is not failed - ignore_errors: true - retries: 10 - -- name: remove the VPC - ec2_vpc_net: - name: "{{ resource_prefix }}-vpc" - cidr_block: 10.55.77.0/24 - state: absent - register: removed - until: removed is not failed - ignore_errors: true - retries: 10 diff --git a/tests/integration/targets/autoscaling_group/roles/ec2_asg/tasks/env_setup.yml b/tests/integration/targets/autoscaling_group/roles/ec2_asg/tasks/env_setup.yml deleted file mode 100644 index 53e3b88a8be..00000000000 --- a/tests/integration/targets/autoscaling_group/roles/ec2_asg/tasks/env_setup.yml +++ /dev/null @@ -1,54 +0,0 @@ -- name: Run ec2_asg integration tests. - run_once: '{{ ec2_asg_setup_run_once }}' - block: - - # Set up the testing dependencies: VPC, subnet, security group, and two launch configurations - - name: Create VPC for use in testing - ec2_vpc_net: - name: "{{ resource_prefix }}-vpc" - cidr_block: 10.55.77.0/24 - tenancy: default - register: testing_vpc - - - name: Create internet gateway for use in testing - ec2_vpc_igw: - vpc_id: "{{ testing_vpc.vpc.id }}" - state: present - register: igw - - - name: Create subnet for use in testing - ec2_vpc_subnet: - state: present - vpc_id: "{{ testing_vpc.vpc.id }}" - cidr: 10.55.77.0/24 - az: "{{ aws_region }}a" - resource_tags: - Name: "{{ resource_prefix }}-subnet" - register: testing_subnet - - - name: create routing rules - ec2_vpc_route_table: - vpc_id: "{{ testing_vpc.vpc.id }}" - tags: - created: "{{ resource_prefix }}-route" - routes: - - dest: 0.0.0.0/0 - gateway_id: "{{ igw.gateway_id }}" - subnets: - - "{{ testing_subnet.subnet.id }}" - - - name: create a security group with the vpc created in the ec2_setup - ec2_group: - name: "{{ resource_prefix }}-sg" - description: a security group for ansible tests - vpc_id: "{{ testing_vpc.vpc.id }}" - rules: - - proto: tcp - from_port: 22 - to_port: 22 - cidr_ip: 0.0.0.0/0 - - proto: tcp - from_port: 80 - to_port: 80 - cidr_ip: 0.0.0.0/0 - register: sg diff --git a/tests/integration/targets/autoscaling_group/roles/ec2_asg/tasks/instance_detach.yml b/tests/integration/targets/autoscaling_group/roles/ec2_asg/tasks/instance_detach.yml deleted file mode 100644 index be4b9aec426..00000000000 --- a/tests/integration/targets/autoscaling_group/roles/ec2_asg/tasks/instance_detach.yml +++ /dev/null @@ -1,277 +0,0 @@ -- name: Running instance detach tests - block: - #---------------------------------------------------------------------- - - name: create a launch configuration - ec2_lc: - name: "{{ resource_prefix }}-lc-detach-test" - image_id: "{{ ec2_ami_id }}" - region: "{{ aws_region }}" - instance_type: t2.micro - assign_public_ip: yes - register: create_lc - - - name: ensure that lc is created - assert: - that: - - create_lc is changed - - create_lc.failed is false - - '"autoscaling:CreateLaunchConfiguration" in create_lc.resource_actions' - - #---------------------------------------------------------------------- - - - name: create a AutoScalingGroup to be used for instance_detach test - check_mode - ec2_asg: - name: "{{ resource_prefix }}-asg-detach-test" - launch_config_name: "{{ resource_prefix }}-lc-detach-test" - health_check_period: 60 - health_check_type: ELB - replace_all_instances: yes - min_size: 3 - max_size: 6 - desired_capacity: 3 - region: "{{ aws_region }}" - register: create_asg - check_mode: true - - - assert: - that: - - create_asg is changed - - create_asg is not failed - - '"autoscaling:CreateAutoScalingGroup" not in create_asg.resource_actions' - - - name: create a AutoScalingGroup to be used for instance_detach test - ec2_asg: - name: "{{ resource_prefix }}-asg-detach-test" - launch_config_name: "{{ resource_prefix }}-lc-detach-test" - health_check_period: 60 - health_check_type: ELB - replace_all_instances: yes - min_size: 3 - max_size: 6 - desired_capacity: 3 - region: "{{ aws_region }}" - register: create_asg - - - name: ensure that AutoScalingGroup is created - assert: - that: - - create_asg is changed - - create_asg.failed is false - - create_asg.instances | length == 3 - - create_asg.desired_capacity == 3 - - create_asg.in_service_instances == 3 - - '"autoscaling:CreateAutoScalingGroup" in create_asg.resource_actions' - - - name: gather info about asg, get instance ids - ec2_asg_info: - name: "{{ resource_prefix }}-asg-detach-test" - register: asg_info - - set_fact: - init_instance_1: "{{ asg_info.results[0].instances[0].instance_id }}" - init_instance_2: "{{ asg_info.results[0].instances[1].instance_id }}" - init_instance_3: "{{ asg_info.results[0].instances[2].instance_id }}" - - - name: Gather information about recently detached instances - amazon.aws.ec2_instance_info: - instance_ids: - - "{{ init_instance_1 }}" - - "{{ init_instance_2 }}" - - "{{ init_instance_3 }}" - register: instances_info - - # assert that there are 3 instances running in the AutoScalingGroup - - assert: - that: - - asg_info.results[0].instances | length == 3 - - "'{{ instances_info.instances[0].state.name }}' == 'running'" - - "'{{ instances_info.instances[1].state.name }}' == 'running'" - - "'{{ instances_info.instances[2].state.name }}' == 'running'" - - #---------------------------------------------------------------------- - - - name: detach 2 instance from the asg and replace with other instances - check_mode - ec2_asg: - name: "{{ resource_prefix }}-asg-detach-test" - launch_config_name: "{{ resource_prefix }}-lc-detach-test" - health_check_period: 60 - health_check_type: ELB - min_size: 3 - max_size: 3 - desired_capacity: 3 - region: "{{ aws_region }}" - detach_instances: - - '{{ init_instance_1 }}' - - '{{ init_instance_2 }}' - register: detach_result - check_mode: true - - - assert: - that: - - detach_result is changed - - detach_result is not failed - - '"autoscaling:DetachInstances" not in detach_result.resource_actions' - - - name: detach 2 instance from the asg and replace with other instances - ec2_asg: - name: "{{ resource_prefix }}-asg-detach-test" - launch_config_name: "{{ resource_prefix }}-lc-detach-test" - health_check_period: 60 - health_check_type: ELB - min_size: 3 - max_size: 3 - desired_capacity: 3 - region: "{{ aws_region }}" - detach_instances: - - '{{ init_instance_1 }}' - - '{{ init_instance_2 }}' - - # pause to allow completion of instance replacement - - name: Pause for 30 seconds - wait_for: - timeout: 30 - - # gather info about asg and get instance ids - - ec2_asg_info: - name: "{{ resource_prefix }}-asg-detach-test" - register: asg_info_replaced - - set_fact: - instance_replace_1: "{{ asg_info_replaced.results[0].instances[0].instance_id }}" - instance_replace_2: "{{ asg_info_replaced.results[0].instances[1].instance_id }}" - instance_replace_3: "{{ asg_info_replaced.results[0].instances[2].instance_id }}" - - # create a list of instance currently attached to asg - - set_fact: - asg_instance_detach_replace: "{{ asg_info_replaced.results[0].instances | map(attribute='instance_id') | list }}" - - - name: Gather information about recently detached instances - amazon.aws.ec2_instance_info: - instance_ids: - - "{{ init_instance_1 }}" - - "{{ init_instance_2 }}" - register: detached_instances_info - - # assert that - # there are 3 still instances in the AutoScalingGroup - # two specified instances are detached and still running independently(not terminated) - - assert: - that: - - asg_info_replaced.results[0].desired_capacity == 3 - - asg_info_replaced.results[0].instances | length == 3 - - "'{{ init_instance_1 }}' not in {{ asg_instance_detach_replace }}" - - "'{{ init_instance_2 }}' not in {{ asg_instance_detach_replace }}" - - "'{{ detached_instances_info.instances[0].state.name }}' == 'running'" - - "'{{ detached_instances_info.instances[1].state.name }}' == 'running'" - - #---------------------------------------------------------------------- - - # detach 2 instances from the asg and reduce the desired capacity from 3 to 1 - - name: detach 2 instance from the asg and reduce the desired capacity from 3 to 1 - ec2_asg: - name: "{{ resource_prefix }}-asg-detach-test" - launch_config_name: "{{ resource_prefix }}-lc-detach-test" - health_check_period: 60 - health_check_type: ELB - min_size: 1 - max_size: 5 - desired_capacity: 3 - region: "{{ aws_region }}" - decrement_desired_capacity: true - detach_instances: - - '{{ instance_replace_1 }}' - - '{{ instance_replace_2 }}' - - - name: Pause for 30 seconds to allow completion of above task - wait_for: - timeout: 30 - - # gather information about asg and get instance id - - ec2_asg_info: - name: "{{ resource_prefix }}-asg-detach-test" - register: asg_info_decrement - - set_fact: - instance_detach_decrement: "{{ asg_info_decrement.results[0].instances[0].instance_id }}" - # create a list of instance ids from info result and set variable value to instance ID - - set_fact: - asg_instance_detach_decrement: "{{ asg_info_decrement.results[0].instances | map(attribute='instance_id') | list }}" - - - name: Gather information about recently detached instances - amazon.aws.ec2_instance_info: - instance_ids: - - "{{ instance_replace_1 }}" - - "{{ instance_replace_2 }}" - register: detached_instances_info - - # assert that - # detached instances are not replaced and there is only 1 instance in the AutoScalingGroup - # desired capacity is reduced to 1 - # detached instances are not terminated - - assert: - that: - - asg_info_decrement.results[0].instances | length == 1 - - asg_info_decrement.results[0].desired_capacity == 1 - - "'{{ instance_replace_1 }}' not in {{ asg_instance_detach_decrement }}" - - "'{{ instance_replace_2 }}' not in {{ asg_instance_detach_decrement }}" - - "'{{ detached_instances_info.instances[0].state.name }}' == 'running'" - - "'{{ detached_instances_info.instances[1].state.name }}' == 'running'" - - "'{{ instance_replace_3 }}' == '{{ instance_detach_decrement }}'" - - #---------------------------------------------------------------------- - - always: - - - name: terminate any instances created during this test - amazon.aws.ec2_instance: - instance_ids: - - "{{ item }}" - state: absent - loop: - - "{{ init_instance_1 }}" - - "{{ init_instance_2 }}" - - "{{ init_instance_3 }}" - - "{{ instance_replace_1 }}" - - "{{ instance_replace_2 }}" - - "{{ instance_replace_3 }}" - - - name: kill asg created in this test - check_mode - ec2_asg: - name: "{{ resource_prefix }}-asg-detach-test" - state: absent - register: removed - check_mode: true - - - assert: - that: - - removed is changed - - removed is not failed - - '"autoscaling:DeleteAutoScalingGroup" not in removed.resource_actions' - - - name: kill asg created in this test - ec2_asg: - name: "{{ resource_prefix }}-asg-detach-test" - state: absent - register: removed - until: removed is not failed - ignore_errors: yes - retries: 10 - - - name: kill asg created in this test - check_mode (idempotent) - ec2_asg: - name: "{{ resource_prefix }}-asg-detach-test" - state: absent - register: removed - check_mode: true - - - assert: - that: - - removed is not changed - - removed is not failed - - '"autoscaling:DeleteAutoScalingGroup" not in removed.resource_actions' - - - name: remove launch config created in this test - ec2_lc: - name: "{{ resource_prefix }}-lc-detach-test" - state: absent - register: removed - until: removed is not failed - ignore_errors: yes - retries: 10 diff --git a/tests/integration/targets/autoscaling_group/roles/ec2_asg/tasks/main.yml b/tests/integration/targets/autoscaling_group/roles/ec2_asg/tasks/main.yml deleted file mode 100644 index 3f6e1eb1b02..00000000000 --- a/tests/integration/targets/autoscaling_group/roles/ec2_asg/tasks/main.yml +++ /dev/null @@ -1,40 +0,0 @@ ---- -# Beware: most of our tests here are run in parallel. -# To add new tests you'll need to add a new host to the inventory and a matching -# '{{ inventory_hostname }}'.yml file in roles/ec2_asg/tasks/ - -- name: "Wrap up all tests and setup AWS credentials" - module_defaults: - group/aws: - aws_access_key: "{{ aws_access_key }}" - aws_secret_key: "{{ aws_secret_key }}" - security_token: "{{ security_token | default(omit) }}" - region: "{{ aws_region }}" - aws_config: - retries: - # Unfortunately AWSRetry doesn't support paginators and boto3's paginators - # don't support any configuration of the delay between retries. - max_attempts: 20 - collections: - - community.aws - block: - - debug: - msg: "{{ inventory_hostname }} start: {{ lookup('pipe','date') }}" - - include_tasks: '{{ inventory_hostname }}.yml' - - debug: - msg: "{{ inventory_hostname }} finish: {{ lookup('pipe','date') }}" - - always: - - set_fact: - _role_complete: True - - vars: - completed_hosts: '{{ ansible_play_hosts_all | map("extract", hostvars, "_role_complete") | list | select("defined") | list | length }}' - hosts_in_play: '{{ ansible_play_hosts_all | length }}' - debug: - msg: "{{ completed_hosts }} of {{ hosts_in_play }} complete" - - include_tasks: env_cleanup.yml - vars: - completed_hosts: '{{ ansible_play_hosts_all | map("extract", hostvars, "_role_complete") | list | select("defined") | list | length }}' - hosts_in_play: '{{ ansible_play_hosts_all | length }}' - when: - - completed_hosts == hosts_in_play diff --git a/tests/integration/targets/autoscaling_group/roles/ec2_asg/tasks/tag_operations.yml b/tests/integration/targets/autoscaling_group/roles/ec2_asg/tasks/tag_operations.yml deleted file mode 100644 index 04a010aa9d3..00000000000 --- a/tests/integration/targets/autoscaling_group/roles/ec2_asg/tasks/tag_operations.yml +++ /dev/null @@ -1,370 +0,0 @@ -- name: Running AutoScalingGroup Tag operations test - block: - #---------------------------------------------------------------------- - - name: create a launch configuration - ec2_lc: - name: "{{ resource_prefix }}-lc-tag-test" - image_id: "{{ ec2_ami_id }}" - region: "{{ aws_region }}" - instance_type: t2.micro - assign_public_ip: yes - register: create_lc - - - name: ensure that lc is created - assert: - that: - - create_lc is changed - - create_lc.failed is false - - '"autoscaling:CreateLaunchConfiguration" in create_lc.resource_actions' - - #---------------------------------------------------------------------- - - name: create a AutoScalingGroup to be used for tag_operations test - ec2_asg: - name: "{{ resource_prefix }}-asg-tag-test" - launch_config_name: "{{ resource_prefix }}-lc-tag-test" - health_check_period: 60 - health_check_type: ELB - replace_all_instances: yes - min_size: 1 - max_size: 1 - desired_capacity: 1 - region: "{{ aws_region }}" - register: create_asg - - - name: ensure that AutoScalingGroup is created - assert: - that: - - create_asg is changed - - create_asg.failed is false - - '"autoscaling:CreateAutoScalingGroup" in create_asg.resource_actions' - - #---------------------------------------------------------------------- - - - name: Get asg info - ec2_asg_info: - name: "{{ resource_prefix }}-asg-tag-test" - register: info_result - - - assert: - that: - - info_result.results[0].tags | length == 0 - - - name: Tag asg - check_mode - ec2_asg: - name: "{{ resource_prefix }}-asg-tag-test" - tags: - - tag_a: 'value 1' - propagate_at_launch: no - - tag_b: 'value 2' - propagate_at_launch: yes - register: output - check_mode: true - - - assert: - that: - - output is changed - - output is not failed - - '"autoscaling:CreateOrUpdateTags" not in output.resource_actions' - - - name: Tag asg - ec2_asg: - name: "{{ resource_prefix }}-asg-tag-test" - tags: - - tag_a: 'value 1' - propagate_at_launch: no - - tag_b: 'value 2' - propagate_at_launch: yes - register: output - - - assert: - that: - - "output.tags | length == 2" - - output is changed - - - name: Re-Tag asg (different order) - ec2_asg: - name: "{{ resource_prefix }}-asg-tag-test" - tags: - - tag_b: 'value 2' - propagate_at_launch: yes - - tag_a: 'value 1' - propagate_at_launch: no - register: output - - - assert: - that: - - "output.tags | length == 2" - - output is not changed - - - name: Re-Tag asg new tags - ec2_asg: - name: "{{ resource_prefix }}-asg-tag-test" - tags: - - tag_c: 'value 3' - propagate_at_launch: no - purge_tags: true - register: output - - - assert: - that: - - "output.tags | length == 1" - - output is changed - - - name: Re-Tag asg update propagate_at_launch - ec2_asg: - name: "{{ resource_prefix }}-asg-tag-test" - tags: - - tag_c: 'value 3' - propagate_at_launch: yes - register: output - - - assert: - that: - - "output.tags | length == 1" - - output is changed - - - name: Remove all tags - ec2_asg: - name: "{{ resource_prefix }}-asg-tag-test" - tags: [] - purge_tags: true - register: add_empty - - - name: Get asg info - ec2_asg_info: - name: "{{ resource_prefix }}-asg-tag-test" - register: info_result - # create a list of tag_keys from info result - - set_fact: - tag_keys: "{{ info_result.results[0].tags | map(attribute='key') | list }}" - - - assert: - that: - - add_empty is changed - - info_result.results[0].tags | length == 0 - - '"autoscaling:CreateOrUpdateTags" not in add_empty.resource_actions' - - '"autoscaling:DeleteTags" in add_empty.resource_actions' - - - name: Add 4 new tags - do not purge existing tags - ec2_asg: - name: "{{ resource_prefix }}-asg-tag-test" - tags: - - lowercase spaced: "hello cruel world" - propagate_at_launch: no - - Title Case: "Hello Cruel World" - propagate_at_launch: yes - - CamelCase: "SimpleCamelCase" - propagate_at_launch: yes - - snake_case: "simple_snake_case" - propagate_at_launch: no - register: add_result - - - name: Get asg info - ec2_asg_info: - name: "{{ resource_prefix }}-asg-tag-test" - register: info_result - # create a list of tag_keys from info result - - set_fact: - tag_keys: "{{ info_result.results[0].tags | map(attribute='key') | list }}" - - - assert: - that: - - add_result is changed - - info_result.results[0].tags | length == 4 - - '"lowercase spaced" in tag_keys' - - '"Title Case" in tag_keys' - - '"CamelCase" in tag_keys' - - '"snake_case" in tag_keys' - - '"autoscaling:CreateOrUpdateTags" in add_result.resource_actions' - - - name: Add 4 new tags - do not purge existing tags - idempotency - ec2_asg: - name: "{{ resource_prefix }}-asg-tag-test" - tags: - - lowercase spaced: "hello cruel world" - propagate_at_launch: no - - Title Case: "Hello Cruel World" - propagate_at_launch: yes - - CamelCase: "SimpleCamelCase" - propagate_at_launch: yes - - snake_case: "simple_snake_case" - propagate_at_launch: no - register: add_result - - - name: Get asg info - ec2_asg_info: - name: "{{ resource_prefix }}-asg-tag-test" - register: info_result - - - assert: - that: - - add_result is not changed - - info_result.results[0].tags | length == 4 - - '"autoscaling:CreateOrUpdateTags" not in add_result.resource_actions' - - - name: Add 2 new tags - purge existing tags - ec2_asg: - name: "{{ resource_prefix }}-asg-tag-test" - tags: - - tag_a: 'val_a' - propagate_at_launch: no - - tag_b: 'val_b' - propagate_at_launch: yes - purge_tags: true - register: add_purge_result - - - name: Get asg info - ec2_asg_info: - name: "{{ resource_prefix }}-asg-tag-test" - register: info_result - # create a list of tag_keys from info result - - set_fact: - tag_keys: "{{ info_result.results[0].tags | map(attribute='key') | list }}" - - - assert: - that: - - add_purge_result is changed - - info_result.results[0].tags | length == 2 - - '"tag_a" in tag_keys' - - '"tag_b" in tag_keys' - - '"lowercase spaced" not in tag_keys' - - '"Title Case" not in tag_keys' - - '"CamelCase" not in tag_keys' - - '"snake_case" not in tag_keys' - - '"autoscaling:CreateOrUpdateTags" in add_purge_result.resource_actions' - - - name: Re-tag ASG - modify values - ec2_asg: - name: "{{ resource_prefix }}-asg-tag-test" - tags: - - tag_a: 'new_val_a' - propagate_at_launch: no - - tag_b: 'new_val_b' - propagate_at_launch: yes - register: add_purge_result - - - name: Get asg info - ec2_asg_info: - name: "{{ resource_prefix }}-asg-tag-test" - register: info_result - # create a list of tag_keys and tag_values from info result - - set_fact: - tag_keys: "{{ info_result.results[0].tags | map(attribute='key') | list }}" - - set_fact: - tag_values: "{{ info_result.results[0].tags | map(attribute='value') | list }}" - - - - assert: - that: - - add_purge_result is changed - - info_result.results[0].tags | length == 2 - - '"tag_a" in tag_keys' - - '"tag_b" in tag_keys' - - '"new_val_a" in tag_values' - - '"new_val_b" in tag_values' - - '"lowercase spaced" not in tag_keys' - - '"Title Case" not in tag_keys' - - '"CamelCase" not in tag_keys' - - '"snake_case" not in tag_keys' - - '"autoscaling:CreateOrUpdateTags" in add_purge_result.resource_actions' - - - name: Add 2 more tags - do not purge existing tags - ec2_asg: - name: "{{ resource_prefix }}-asg-tag-test" - tags: - - lowercase spaced: "hello cruel world" - propagate_at_launch: no - - Title Case: "Hello Cruel World" - propagate_at_launch: yes - register: add_result - - - name: Get asg info - ec2_asg_info: - name: "{{ resource_prefix }}-asg-tag-test" - register: info_result - # create a list of tag_keys from info result - - set_fact: - tag_keys: "{{ info_result.results[0].tags | map(attribute='key') | list }}" - - - assert: - that: - - add_result is changed - - info_result.results[0].tags | length == 4 - - '"tag_a" in tag_keys' - - '"tag_b" in tag_keys' - - '"lowercase spaced" in tag_keys' - - '"Title Case" in tag_keys' - - '"autoscaling:CreateOrUpdateTags" in add_result.resource_actions' - - - name: Add empty tags with purge set to false to assert that existing tags are retained - ec2_asg: - name: "{{ resource_prefix }}-asg-tag-test" - tags: [] - purge_tags: false - register: add_empty - - - name: Get asg info - ec2_asg_info: - name: "{{ resource_prefix }}-asg-tag-test" - register: info_result - # create a list of tag_keys from info result - - set_fact: - tag_keys: "{{ info_result.results[0].tags | map(attribute='key') | list }}" - - - assert: - that: - - add_empty is not changed - - info_result.results[0].tags | length == 4 - - '"tag_a" in tag_keys' - - '"tag_b" in tag_keys' - - '"lowercase spaced" in tag_keys' - - '"Title Case" in tag_keys' - - '"autoscaling:CreateOrUpdateTags" not in add_empty.resource_actions' - - - name: Add empty tags with purge set to true to assert that existing tags are removed - ec2_asg: - name: "{{ resource_prefix }}-asg-tag-test" - tags: [] - purge_tags: true - register: add_empty - - - name: Get asg info - ec2_asg_info: - name: "{{ resource_prefix }}-asg-tag-test" - register: info_result - # create a list of tag_keys from info result - - set_fact: - tag_keys: "{{ info_result.results[0].tags | map(attribute='key') | list }}" - - - assert: - that: - - add_empty is changed - - info_result.results[0].tags | length == 0 - - '"tag_a" not in tag_keys' - - '"tag_b" not in tag_keys' - - '"lowercase spaced" not in tag_keys' - - '"Title Case" not in tag_keys' - - '"autoscaling:CreateOrUpdateTags" not in add_empty.resource_actions' - - '"autoscaling:DeleteTags" in add_empty.resource_actions' - - #---------------------------------------------------------------------- - - always: - - - name: kill asg created in this test - ec2_asg: - name: "{{ resource_prefix }}-asg-tag-test" - state: absent - register: removed - until: removed is not failed - ignore_errors: yes - retries: 10 - - - name: remove launch config created in this test - ec2_lc: - name: "{{ resource_prefix }}-lc-tag-test" - state: absent - register: removed - until: removed is not failed - ignore_errors: yes - retries: 10 diff --git a/tests/integration/targets/autoscaling_group/runme.sh b/tests/integration/targets/autoscaling_group/runme.sh deleted file mode 100755 index aa324772bbe..00000000000 --- a/tests/integration/targets/autoscaling_group/runme.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env bash -# -# Beware: most of our tests here are run in parallel. -# To add new tests you'll need to add a new host to the inventory and a matching -# '{{ inventory_hostname }}'.yml file in roles/ec2_instance/tasks/ - - -set -eux - -export ANSIBLE_ROLES_PATH=../ - -ansible-playbook main.yml -i inventory "$@"