Repository: ansible/ansible

Issue:
Add launch_type parameter for ecs_service module
<!---
Verify first that your issue/request is not already reported on GitHub.
Also test if the latest release, and master branch are affected too.
-->

##### ISSUE TYPE
<!--- Pick one below and delete the rest -->
 - Feature Idea

##### COMPONENT NAME
<!---
Name of the module, plugin, task or feature
Do not include extra details here, e.g. "vyos_command" not "the network module vyos_command" or the full path
-->
ecs_service

##### ANSIBLE VERSION
<!--- Paste verbatim output from "ansible --version" between quotes below -->
```
$ ansible --version
ansible 2.4.2.0
  config file = None
  configured module search path = [u'/Users/ramonamis/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /Users/ramonamis/.virtualenvs/devops/lib/python2.7/site-packages/ansible
  executable location = /Users/ramonamis/.virtualenvs/devops/bin/ansible
  python version = 2.7.10 (default, Jul 15 2017, 17:16:57) [GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.31)]
```

##### CONFIGURATION
<!---
If using Ansible 2.4 or above, paste the results of "ansible-config dump --only-changed"
Otherwise, mention any settings you have changed/added/removed in ansible.cfg
(or using the ANSIBLE_* environment variables).
-->

##### OS / ENVIRONMENT
<!---
Mention the OS you are running Ansible from, and the OS you are
managing, or say "N/A" for anything that is not platform-specific.
Also mention the specific version of what you are trying to control,
e.g. if this is a network bug the version of firmware on the network device.
-->
N/A
##### SUMMARY
<!--- Explain the problem briefly -->
By default, the ecs_service module is launching services with launch_type EC2. At the moment it is impossible to use launch_type: "FARGATE" in order to migrate to fargate.

For more details you can check: https://docs.aws.amazon.com/cli/latest/reference/ecs/create-service.html

##### STEPS TO REPRODUCE
<!---
For bugs, show exactly how to reproduce the problem, using a minimal test-case.
For new features, show how the feature would be used.
-->

<!--- Paste example playbooks or commands between quotes below -->
It could look something like this: 
```yaml
          - name: deploy service
            ecs_service:
              region: "{{ aws_region }}"
              name: "{{ ecs_task_family }}"
              load_balancers: "{{ ecs_task_load_balancers }}"
              launch_type: "FARGATE"
              state: present
              task_definition: "{{ ecs_task_family  }}"
              cluster: "{{ ecs_cluster_name }}"
              desired_count: "{{ ecs_task_desired_count }}"
```

<!--- You can also paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- What did you expect to happen when running the steps above? -->
Be able to deploy ecs services to fargate
##### ACTUAL RESULTS
<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->
I am not able to deploy ecs services with fargate
<!--- Paste verbatim command output between quotes below -->
```
fatal: [localhost]: FAILED! => {"attempts": 10, "changed": false, "msg": "Unsupported parameters for (ecs_service) module: launch_type Supported parameters include: aws_access_key,aws_secret_key,client_token,cluster,delay,deployment_configuration,desired_count,ec2_url,load_balancers,name,placement_constraints,placement_strategy,profile,region,repeat,role,security_token,state,task_definition,validate_certs"}
```


Discussion:
Files identified in the description:
* [lib/ansible/modules/cloud/amazon/ecs_service.py](https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/cloud/amazon/ecs_service.py)

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
cc @java1guy @kaczynskid @willthames @zacblazic
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
likewise, an implementation has to be done for the ecs_taskdefinition to make the task compatible with fargate too 

Resolving patch:
diff --git a/lib/ansible/modules/cloud/amazon/ecs_service.py b/lib/ansible/modules/cloud/amazon/ecs_service.py
--- a/lib/ansible/modules/cloud/amazon/ecs_service.py
+++ b/lib/ansible/modules/cloud/amazon/ecs_service.py
@@ -103,6 +103,12 @@
           - I(network_configuration) has two keys, I(subnets), a list of subnet IDs to which the task is attached and I(security_groups),
             a list of group names or group IDs for the task
         version_added: 2.6
+    launch_type:
+        description:
+          - The launch type on which to run your service
+        required: false
+        version_added: 2.7
+        choices: ["EC2", "FARGATE"]
 extends_documentation_fragment:
     - aws
     - ec2
@@ -357,7 +363,8 @@ def is_matching_service(self, expected, existing):
 
     def create_service(self, service_name, cluster_name, task_definition, load_balancers,
                        desired_count, client_token, role, deployment_configuration,
-                       placement_constraints, placement_strategy, network_configuration):
+                       placement_constraints, placement_strategy, network_configuration,
+                       launch_type):
         params = dict(
             cluster=cluster_name,
             serviceName=service_name,
@@ -368,9 +375,12 @@ def create_service(self, service_name, cluster_name, task_definition, load_balan
             role=role,
             deploymentConfiguration=deployment_configuration,
             placementConstraints=placement_constraints,
-            placementStrategy=placement_strategy)
+            placementStrategy=placement_strategy
+        )
         if network_configuration:
             params['networkConfiguration'] = network_configuration
+        if launch_type:
+            params['launchType'] = launch_type
         response = self.ecs.create_service(**params)
         return self.jsonize(response['service'])
 
@@ -431,12 +441,14 @@ def main():
         deployment_configuration=dict(required=False, default={}, type='dict'),
         placement_constraints=dict(required=False, default=[], type='list'),
         placement_strategy=dict(required=False, default=[], type='list'),
-        network_configuration=dict(required=False, type='dict')
+        network_configuration=dict(required=False, type='dict'),
+        launch_type=dict(required=False, choices=['EC2', 'FARGATE'])
     ))
 
     module = AnsibleAWSModule(argument_spec=argument_spec,
                               supports_check_mode=True,
-                              required_if=[('state', 'present', ['task_definition', 'desired_count'])],
+                              required_if=[('state', 'present', ['task_definition', 'desired_count']),
+                                           ('launch_type', 'FARGATE', ['network_configuration'])],
                               required_together=[['load_balancers', 'role']])
 
     service_mgr = EcsServiceManager(module)
@@ -458,10 +470,16 @@ def main():
         module.fail_json(msg="Exception describing service '" + module.params['name'] + "' in cluster '" + module.params['cluster'] + "': " + str(e))
 
     results = dict(changed=False)
+
+    if module.params['launch_type']:
+        if not module.botocore_at_least('1.8.4'):
+            module.fail_json(msg='botocore needs to be version 1.8.4 or higher to use launch_type')
+
     if module.params['state'] == 'present':
 
         matching = False
         update = False
+
         if existing and 'status' in existing and existing['status'] == "ACTIVE":
             if service_mgr.is_matching_service(module.params, existing):
                 matching = True
@@ -491,17 +509,21 @@ def main():
                         if 'containerPort' in loadBalancer:
                             loadBalancer['containerPort'] = int(loadBalancer['containerPort'])
                     # doesn't exist. create it.
-                    response = service_mgr.create_service(module.params['name'],
-                                                          module.params['cluster'],
-                                                          module.params['task_definition'],
-                                                          loadBalancers,
-                                                          module.params['desired_count'],
-                                                          clientToken,
-                                                          role,
-                                                          deploymentConfiguration,
-                                                          module.params['placement_constraints'],
-                                                          module.params['placement_strategy'],
-                                                          network_configuration)
+                    try:
+                        response = service_mgr.create_service(module.params['name'],
+                                                              module.params['cluster'],
+                                                              module.params['task_definition'],
+                                                              loadBalancers,
+                                                              module.params['desired_count'],
+                                                              clientToken,
+                                                              role,
+                                                              deploymentConfiguration,
+                                                              module.params['placement_constraints'],
+                                                              module.params['placement_strategy'],
+                                                              network_configuration,
+                                                              module.params['launch_type'])
+                    except botocore.exceptions.ClientError as e:
+                        module.fail_json_aws(e, msg="Couldn't create service")
 
                 results['service'] = response
 
@@ -526,7 +548,7 @@ def main():
                             module.params['cluster']
                         )
                     except botocore.exceptions.ClientError as e:
-                        module.fail_json(msg=e.message)
+                        module.fail_json_aws(e, msg="Couldn't delete service")
                 results['changed'] = True
 
     elif module.params['state'] == 'deleting':
diff --git a/lib/ansible/modules/cloud/amazon/ecs_taskdefinition.py b/lib/ansible/modules/cloud/amazon/ecs_taskdefinition.py
--- a/lib/ansible/modules/cloud/amazon/ecs_taskdefinition.py
+++ b/lib/ansible/modules/cloud/amazon/ecs_taskdefinition.py
@@ -73,6 +73,24 @@
         description:
             - A list of names of volumes to be attached
         required: False
+    launch_type:
+        description:
+            - The launch type on which to run your task
+        required: false
+        version_added: 2.7
+        choices: ["EC2", "FARGATE"]
+    cpu:
+        description:
+            - The number of cpu units used by the task. If using the EC2 launch type, this field is optional and any value can be used.
+              If using the Fargate launch type, this field is required and you must use one of [256, 512, 1024, 2048, 4096]
+        required: false
+        version_added: 2.7
+    memory:
+        description:
+            - The amount (in MiB) of memory used by the task. If using the EC2 launch type, this field is optional and any value can be used.
+              If using the Fargate launch type, this field is required and is limited by the cpu
+        required: false
+        version_added: 2.7
 extends_documentation_fragment:
     - aws
     - ec2
@@ -118,6 +136,36 @@
     family: test-cluster-taskdef
     state: present
   register: task_output
+
+- name: Create task definition
+  ecs_taskdefinition:
+    family: nginx
+    containers:
+    - name: nginx
+      essential: true
+      image: "nginx"
+      portMappings:
+      - containerPort: 8080
+        hostPort:      8080
+      cpu: 512
+      memory: 1GB
+    state: present
+
+- name: Create task definition
+  ecs_taskdefinition:
+    family: nginx
+    containers:
+    - name: nginx
+      essential: true
+      image: "nginx"
+      portMappings:
+      - containerPort: 8080
+        hostPort:      8080
+    launch_type: FARGATE
+    cpu: 512
+    memory: 1GB
+    state: present
+    network_mode: awsvpc
 '''
 RETURN = '''
 taskdefinition:
@@ -153,7 +201,7 @@ def describe_task(self, task_name):
         except botocore.exceptions.ClientError:
             return None
 
-    def register_task(self, family, task_role_arn, network_mode, container_definitions, volumes):
+    def register_task(self, family, task_role_arn, network_mode, container_definitions, volumes, launch_type, cpu, memory):
         validated_containers = []
 
         # Ensures the number parameters are int as required by boto
@@ -174,12 +222,22 @@ def register_task(self, family, task_role_arn, network_mode, container_definitio
 
             validated_containers.append(container)
 
+        params = dict(
+            family=family,
+            taskRoleArn=task_role_arn,
+            networkMode=network_mode,
+            containerDefinitions=container_definitions,
+            volumes=volumes
+        )
+        if cpu:
+            params['cpu'] = cpu
+        if memory:
+            params['memory'] = memory
+        if launch_type:
+            params['requiresCompatibilities'] = [launch_type]
+
         try:
-            response = self.ecs.register_task_definition(family=family,
-                                                         taskRoleArn=task_role_arn,
-                                                         networkMode=network_mode,
-                                                         containerDefinitions=container_definitions,
-                                                         volumes=volumes)
+            response = self.ecs.register_task_definition(**params)
         except botocore.exceptions.ClientError as e:
             self.module.fail_json(msg=e.message, **camel_dict_to_snake_dict(e.response))
 
@@ -221,9 +279,14 @@ def deregister_task(self, taskArn):
         response = self.ecs.deregister_task_definition(taskDefinition=taskArn)
         return response['taskDefinition']
 
+    def ecs_api_supports_requirescompatibilities(self):
+        from distutils.version import LooseVersion
+        # Checking to make sure botocore is greater than a specific version.
+        # Support for requiresCompatibilities is only available in versions beyond 1.8.4
+        return LooseVersion(botocore.__version__) >= LooseVersion('1.8.4')
 
-def main():
 
+def main():
     argument_spec = ec2_argument_spec()
     argument_spec.update(dict(
         state=dict(required=True, choices=['present', 'absent']),
@@ -234,9 +297,16 @@ def main():
         containers=dict(required=False, type='list'),
         network_mode=dict(required=False, default='bridge', choices=['bridge', 'host', 'none', 'awsvpc'], type='str'),
         task_role_arn=dict(required=False, default='', type='str'),
-        volumes=dict(required=False, type='list')))
+        volumes=dict(required=False, type='list'),
+        launch_type=dict(required=False, choices=['EC2', 'FARGATE']),
+        cpu=dict(),
+        memory=dict(required=False, type='str')
+    ))
 
-    module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True)
+    module = AnsibleModule(argument_spec=argument_spec,
+                           supports_check_mode=True,
+                           required_if=[('launch_type', 'FARGATE', ['cpu', 'memory'])]
+                           )
 
     if not HAS_BOTO3:
         module.fail_json(msg='boto3 is required.')
@@ -245,6 +315,10 @@ def main():
     task_mgr = EcsTaskManager(module)
     results = dict(changed=False)
 
+    if module.params['launch_type']:
+        if not task_mgr.ecs_api_supports_requirescompatibilities():
+            module.fail_json(msg='botocore needs to be version 1.8.4 or higher to use launch_type')
+
     for container in module.params.get('containers', []):
         for environment in container.get('environment', []):
             environment['value'] = to_text(environment['value'])
@@ -256,6 +330,11 @@ def main():
         if 'family' not in module.params or not module.params['family']:
             module.fail_json(msg="To use task definitions, a family must be specified")
 
+        network_mode = module.params['network_mode']
+        launch_type = module.params['launch_type']
+        if launch_type == 'FARGATE' and network_mode != 'awsvpc':
+            module.fail_json(msg="To use FARGATE launch type, network_mode must be awsvpc")
+
         family = module.params['family']
         existing_definitions_in_family = task_mgr.describe_task_definitions(module.params['family'])
 
@@ -365,7 +444,10 @@ def _task_definition_matches(requested_volumes, requested_containers, existing_t
                                                                    module.params['task_role_arn'],
                                                                    module.params['network_mode'],
                                                                    module.params['containers'],
-                                                                   volumes)
+                                                                   volumes,
+                                                                   module.params['launch_type'],
+                                                                   module.params['cpu'],
+                                                                   module.params['memory'])
             results['changed'] = True
 
     elif module.params['state'] == 'absent':


Test patch:
diff --git a/hacking/aws_config/testing_policies/compute-policy.json b/hacking/aws_config/testing_policies/compute-policy.json
--- a/hacking/aws_config/testing_policies/compute-policy.json
+++ b/hacking/aws_config/testing_policies/compute-policy.json
@@ -109,29 +109,6 @@
                 "arn:aws:ec2:{{aws_region}}:{{aws_account}}:*"
             ]
         },
-        {
-            "Sid": "UnspecifiedCodeRepositories",
-            "Effect": "Allow",
-            "Action": [
-                "ecr:DescribeRepositories",
-                "ecr:CreateRepository"
-            ],
-            "Resource": "*"
-        },
-        {
-            "Sid": "SpecifiedCodeRepositories",
-            "Effect": "Allow",
-            "Action": [
-                "ecr:GetRepositoryPolicy",
-                "ecr:SetRepositoryPolicy",
-                "ecr:DeleteRepository",
-                "ecr:DeleteRepositoryPolicy",
-                "ecr:DeleteRepositoryPolicy"
-            ],
-            "Resource": [
-                "arn:aws:ecr:{{aws_region}}:{{aws_account}}:repository/ansible-*"
-            ]
-        },
 {# According to http://docs.aws.amazon.com/elasticloadbalancing/latest/userguide/load-balancer-authentication-access-control.html #}
 {# Resource level access control is not possible for the new ELB API (providing Application Load Balancer functionality #}
 {# While it remains possible for the old API, there is no distinction of the Actions between old API and new API #}
@@ -238,29 +215,6 @@
                 "arn:aws:iam::{{aws_account}}:role/ecsServiceRole"
             ]
         },
-        {
-          "Sid": "AllowECSManagement",
-          "Effect": "Allow",
-          "Action": [
-            "application-autoscaling:Describe*",
-            "application-autoscaling:PutScalingPolicy",
-            "application-autoscaling:RegisterScalableTarget",
-            "cloudwatch:DescribeAlarms",
-            "cloudwatch:PutMetricAlarm",
-            "ecs:CreateCluster",
-            "ecs:CreateService",
-            "ecs:DeleteCluster",
-            "ecs:DeleteService",
-            "ecs:Describe*",
-            "ecs:DeregisterTaskDefinition",
-            "ecs:List*",
-            "ecs:RegisterTaskDefinition",
-            "ecs:UpdateService"
-          ],
-          "Resource": [
-            "*"
-          ]
-        },
         {
           "Sid": "AllowSESManagement",
           "Effect": "Allow",
diff --git a/test/integration/targets/ecs_cluster/playbooks/network_fail.yml b/test/integration/targets/ecs_cluster/playbooks/network_fail.yml
--- a/test/integration/targets/ecs_cluster/playbooks/network_fail.yml
+++ b/test/integration/targets/ecs_cluster/playbooks/network_fail.yml
@@ -1,5 +1,7 @@
 - hosts: localhost
   connection: local
+  vars:
+     resource_prefix: 'ansible-testing'
 
   tasks:
     - block:
@@ -61,7 +63,7 @@
           ecs_service:
             name: "{{ resource_prefix }}-vpc"
             cluster: "{{ resource_prefix }}"
-            task_definition: "{{ resource_prefix }}"
+            task_definition: "{{ resource_prefix }}-vpc"
             desired_count: 1
             network_configuration:
               subnets:
@@ -79,6 +81,47 @@
               - ecs_service_creation_vpc.failed
               - 'ecs_service_creation_vpc.msg == "botocore needs to be version 1.7.44 or higher to use network configuration"'
 
+        - name: create ecs_service using awsvpc network_configuration and launch_type
+          ecs_service:
+            name: "{{ resource_prefix }}-vpc"
+            cluster: "{{ resource_prefix }}"
+            task_definition: "{{ resource_prefix }}-vpc"
+            desired_count: 1
+            network_configuration:
+              subnets:
+                - subnet-abcd1234
+              groups:
+                - sg-abcd1234
+            launch_type: FARGATE
+            state: present
+            <<: *aws_connection_info
+          register: ecs_service_creation_vpc_launchtype
+          ignore_errors: yes
+
+        - name: check that graceful failure message is returned from ecs_service
+          assert:
+            that:
+              - ecs_service_creation_vpc_launchtype.failed
+              - 'ecs_service_creation_vpc_launchtype.msg == "botocore needs to be version 1.7.44 or higher to use network configuration"'
+
+        - name: create ecs_service with launchtype and missing network_configuration
+          ecs_service:
+            name: "{{ resource_prefix }}-vpc"
+            cluster: "{{ resource_prefix }}"
+            task_definition: "{{ resource_prefix }}-vpc"
+            desired_count: 1
+            launch_type: FARGATE
+            state: present
+            <<: *aws_connection_info
+          register: ecs_service_creation_vpc_launchtype_nonet
+          ignore_errors: yes
+
+        - name: check that graceful failure message is returned from ecs_service
+          assert:
+            that:
+              - ecs_service_creation_vpc_launchtype_nonet.failed
+              - 'ecs_service_creation_vpc_launchtype_nonet.msg == "launch_type is FARGATE but all of the following are missing: network_configuration"'
+
         - name: create ecs_task using awsvpc network_configuration
           ecs_task:
             cluster: "{{ resource_prefix }}-vpc"
@@ -101,6 +144,7 @@
               - ecs_task_creation_vpc.failed
               - 'ecs_task_creation_vpc.msg == "botocore needs to be version 1.7.44 or higher to use network configuration"'
 
+
       always:
         - name: scale down ecs service
           ecs_service:
@@ -138,6 +182,18 @@
             <<: *aws_connection_info
           ignore_errors: yes
 
+        - name: remove ecs task definition vpc
+          ecs_taskdefinition:
+            containers:
+              - name: my_container
+                image: ubuntu
+                memory: 128
+            family: "{{ resource_prefix }}-vpc"
+            revision: "{{ ecs_taskdefinition_creation_vpc.taskdefinition.revision }}"
+            state: absent
+            <<: *aws_connection_info
+          ignore_errors: yes
+
         - name: remove ecs cluster
           ecs_cluster:
             name: "{{ resource_prefix }}"
diff --git a/test/integration/targets/ecs_cluster/playbooks/roles/ecs_cluster/defaults/main.yml b/test/integration/targets/ecs_cluster/playbooks/roles/ecs_cluster/defaults/main.yml
--- a/test/integration/targets/ecs_cluster/playbooks/roles/ecs_cluster/defaults/main.yml
+++ b/test/integration/targets/ecs_cluster/playbooks/roles/ecs_cluster/defaults/main.yml
@@ -1,9 +1,3 @@
-# http://docs.aws.amazon.com/AmazonECS/latest/developerguide/ecs-optimized_AMI.html
-# amzn-ami-2017.09.b-amazon-ecs-optimized
-ecs_agent_images:
-  us-east-1: ami-71ef560b
-  us-east-2: ami-1b8ca37e
-
 ecs_cluster_name: "{{ resource_prefix }}"
 user_data: |
   #!/bin/bash
@@ -33,3 +27,11 @@ ecs_service_placement_strategy:
 ecs_task_container_port: 8080
 ecs_target_group_name: "{{ resource_prefix[:28] }}-tg"
 ecs_load_balancer_name: "{{ resource_prefix[:29] }}-lb"
+ecs_fargate_task_containers:
+- name: "{{ ecs_task_name }}"
+  image: "{{ ecs_task_image_path }}"
+  essential: true
+  portMappings:
+  - containerPort: "{{ ecs_task_container_port }}"
+    hostPort: "{{ ecs_task_host_port|default(0) }}"
+  #mountPoints: "{{ ecs_task_mount_points|default([]) }}"
diff --git a/test/integration/targets/ecs_cluster/playbooks/roles/ecs_cluster/tasks/main.yml b/test/integration/targets/ecs_cluster/playbooks/roles/ecs_cluster/tasks/main.yml
--- a/test/integration/targets/ecs_cluster/playbooks/roles/ecs_cluster/tasks/main.yml
+++ b/test/integration/targets/ecs_cluster/playbooks/roles/ecs_cluster/tasks/main.yml
@@ -123,12 +123,24 @@
         <<: *aws_connection_info
       register: setup_sg
 
+    - name: find a suitable AMI
+      ec2_ami_facts:
+        owner: amazon
+        filters:
+          description: "Amazon Linux AMI* ECS *"
+        <<: *aws_connection_info
+      register: ec2_ami_facts
+
+    - name: set image id fact
+      set_fact:
+        ecs_image_id: "{{ (ec2_ami_facts.images|first).image_id }}"
+
     - name: provision ec2 instance to create an image
       ec2:
         key_name: '{{ ec2_keypair|default(setup_key.key.name) }}'
         instance_type: t2.micro
         state: present
-        image: '{{ ecs_agent_images[aws_region] }}'
+        image: '{{ ecs_image_id }}'
         wait: yes
         user_data: "{{ user_data }}"
         instance_profile_name: ecsInstanceRole
@@ -344,6 +356,11 @@
         that:
           - delete_ecs_service.changed
 
+    - name: assert that deleting ECS service worked
+      assert:
+        that:
+          - delete_ecs_service.changed
+
     - name: create VPC-networked task definition with host port set to 0 (expected to fail)
       ecs_taskdefinition:
         containers: "{{ ecs_task_containers }}"
@@ -382,6 +399,17 @@
         that:
           - "ecs_taskdefinition_facts.network_mode == 'awsvpc'"
 
+    - name: pause to allow service to scale down
+      pause:
+        seconds: 60
+
+    - name: delete ECS service definition
+      ecs_service:
+        state: absent
+        name: "{{ ecs_service_name }}4"
+        cluster: "{{ ecs_cluster_name }}"
+        <<: *aws_connection_info
+      register: delete_ecs_service
 
     - name: create ECS service definition with network configuration
       ecs_service:
@@ -448,7 +476,6 @@
           - "update_ecs_service_with_vpc.service.networkConfiguration.awsvpcConfiguration.subnets|length == 2"
           - "update_ecs_service_with_vpc.service.networkConfiguration.awsvpcConfiguration.securityGroups|length == 1"
 
-
     - name: obtain facts for all ECS services in the cluster
       ecs_service_facts:
         cluster: "{{ ecs_cluster_name }}"
@@ -511,6 +538,103 @@
         task_definition: "{{ ecs_task_name }}-vpc:{{ ecs_task_definition.taskdefinition.revision + 1}}"
         <<: *aws_connection_info
 
+    # ============================================================
+    # Begin tests for Fargate
+
+    - name: create Fargate VPC-networked task definition with host port set to 8080 and unsupported network mode (expected to fail)
+      ecs_taskdefinition:
+        containers: "{{ ecs_fargate_task_containers }}"
+        family: "{{ ecs_task_name }}-vpc"
+        network_mode: bridge
+        launch_type: FARGATE
+        cpu: 512
+        memory: 1024
+        state: present
+        <<: *aws_connection_info
+      vars:
+        ecs_task_host_port: 8080
+      ignore_errors: yes
+      register: ecs_fargate_task_definition_bridged_with_host_port
+
+    - name: check that fargate task definition with bridged networking fails gracefully
+      assert:
+        that:
+          - ecs_fargate_task_definition_bridged_with_host_port is failed
+          - 'ecs_fargate_task_definition_bridged_with_host_port.msg == "To use FARGATE launch type, network_mode must be awsvpc"' 
+
+    - name: create Fargate VPC-networked task definition without CPU or Memory (expected to Fail)
+      ecs_taskdefinition:
+        containers: "{{ ecs_fargate_task_containers }}"
+        family: "{{ ecs_task_name }}-vpc"
+        network_mode: awsvpc
+        launch_type: FARGATE
+        state: present
+        <<: *aws_connection_info
+      ignore_errors: yes
+      register: ecs_fargate_task_definition_vpc_no_mem
+
+    - name: check that fargate task definition without memory or cpu fails gracefully
+      assert:
+        that:
+          - ecs_fargate_task_definition_vpc_no_mem is failed
+          - 'ecs_fargate_task_definition_vpc_no_mem.msg == "launch_type is FARGATE but all of the following are missing: cpu, memory"' 
+
+    - name: create Fargate VPC-networked task definition with CPU or Memory 
+      ecs_taskdefinition:
+        containers: "{{ ecs_fargate_task_containers }}"
+        family: "{{ ecs_task_name }}-vpc"
+        network_mode: awsvpc
+        launch_type: FARGATE
+        cpu: 512
+        memory: 1024
+        state: present
+        <<: *aws_connection_info
+      vars:
+        ecs_task_host_port: 8080
+      register: ecs_fargate_task_definition
+
+    - name: obtain ECS task definition facts
+      ecs_taskdefinition_facts:
+        task_definition: "{{ ecs_task_name }}-vpc:{{ ecs_fargate_task_definition.taskdefinition.revision }}"
+        <<: *aws_connection_info
+
+    - name: create fargate ECS service without network config (expected to fail)
+      ecs_service:
+        state: present
+        name: "{{ ecs_service_name }}4"
+        cluster: "{{ ecs_cluster_name }}"
+        task_definition: "{{ ecs_task_name }}-vpc:{{ ecs_fargate_task_definition.taskdefinition.revision }}"
+        desired_count: 1
+        deployment_configuration: "{{ ecs_service_deployment_configuration }}"
+        launch_type: FARGATE
+        <<: *aws_connection_info
+      register: ecs_fargate_service_network_without_awsvpc
+      ignore_errors: yes
+     
+    - name: assert that using Fargate ECS service fails
+      assert:
+        that:
+          - ecs_fargate_service_network_without_awsvpc is failed
+
+    - name: create fargate ECS service with network config 
+      ecs_service:
+        state: present
+        name: "{{ ecs_service_name }}4"
+        cluster: "{{ ecs_cluster_name }}"
+        task_definition: "{{ ecs_task_name }}-vpc:{{ ecs_fargate_task_definition.taskdefinition.revision }}"
+        desired_count: 1
+        deployment_configuration: "{{ ecs_service_deployment_configuration }}"
+        launch_type: FARGATE
+        network_configuration:
+          subnets: "{{ setup_subnet.results | json_query('[].subnet.id') }}"
+          security_groups:
+            - '{{ setup_sg.group_id }}'
+        <<: *aws_connection_info
+      register: ecs_fargate_service_network_with_awsvpc
+
+    # ============================================================
+    # End tests for Fargate
+
   always:
     # TEAR DOWN: snapshot, ec2 instance, ec2 key pair, security group, vpc
     - name: Announce teardown start
@@ -568,6 +692,18 @@
       ignore_errors: yes
       register: ecs_service_scale_down
 
+    - name: scale down Fargate ECS service
+      ecs_service:
+        state: present
+        name: "{{ ecs_service_name }}4"
+        cluster: "{{ ecs_cluster_name }}"
+        task_definition: "{{ ecs_task_name }}-vpc:{{ ecs_fargate_task_definition.taskdefinition.revision }}"
+        desired_count: 0
+        deployment_configuration: "{{ ecs_service_deployment_configuration }}"
+        <<: *aws_connection_info
+      ignore_errors: yes
+      register: ecs_service_scale_down
+
     - name: pause to allow services to scale down
       pause:
         seconds: 60
@@ -589,6 +725,15 @@
         <<: *aws_connection_info
       ignore_errors: yes
 
+    - name: remove fargate ECS service 
+      ecs_service:
+        state: absent
+        name: "{{ ecs_service_name }}4"
+        cluster: "{{ ecs_cluster_name }}"
+        <<: *aws_connection_info
+      ignore_errors: yes
+      register: ecs_fargate_service_network_with_awsvpc
+ 
     - name: remove ecs task definition
       ecs_taskdefinition:
         containers: "{{ ecs_task_containers }}"
@@ -600,6 +745,17 @@
         ecs_task_host_port: 8080
       ignore_errors: yes
 
+    - name: remove ecs task definition again
+      ecs_taskdefinition:
+        containers: "{{ ecs_task_containers }}"
+        family: "{{ ecs_task_name }}"
+        revision: "{{ ecs_task_definition_again.taskdefinition.revision }}"
+        state: absent
+        <<: *aws_connection_info
+      vars:
+        ecs_task_host_port: 8080
+      ignore_errors: yes
+
     - name: remove second ecs task definition
       ecs_taskdefinition:
         containers: "{{ ecs_task_containers }}"
@@ -611,6 +767,15 @@
         ecs_task_host_port: 8080
       ignore_errors: yes
 
+    - name: remove fargate ecs task definition
+      ecs_taskdefinition:
+        containers: "{{ ecs_fargate_task_containers }}"
+        family: "{{ ecs_task_name }}-vpc"
+        revision: "{{ ecs_fargate_task_definition.taskdefinition.revision }}"
+        state: absent
+        <<: *aws_connection_info
+      ignore_errors: yes
+
     - name: remove load balancer
       elb_application_lb:
         name: "{{ ecs_load_balancer_name }}"