Repository: ansible/ansible

Issue:
ec2_vpc_route_table fails with "AttributeError: 'dict' object has no attribute 'tags'" on route table creation
<!---
Verify first that your issue/request is not already reported on GitHub.
Also test if the latest release, and devel branch are affected too.
Always add information AFTER of these html comments.
-->

##### ISSUE TYPE
<!--- Pick one below and delete the rest -->
 - Bug Report


##### COMPONENT NAME
<!---
Insert below this comment the 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
-->
ec2_vpc_route_table

##### ANSIBLE VERSION
<!--- Paste verbatim output from "ansible --version" between quotes below -->
```
ansible 2.5.0
  config file = /home/vagrant/repo/v2-ansible/ansible.cfg
  configured module search path = [u'/home/vagrant/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/local/lib/python2.7/dist-packages/ansible
  executable location = /usr/local/bin/ansible
  python version = 2.7.13 (default, Jan 19 2017, 14:48:08) [GCC 6.3.0 20170118]
```

##### 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).
-->
```
ANSIBLE_SSH_ARGS(/home/vagrant/repo/v2-ansible/ansible.cfg) = -C -o ControlMaster=auto -o ControlPersist=60s -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no
DEFAULT_HOST_LIST(/home/vagrant/repo/v2-ansible/ansible.cfg) = [u'/home/vagrant/repo/v2-ansible/environment/test']
DEFAULT_REMOTE_USER(/home/vagrant/repo/v2-ansible/ansible.cfg) = {{ ansible_username | default('ansible') }}
HOST_KEY_CHECKING(/home/vagrant/repo/v2-ansible/ansible.cfg) = False
RETRY_FILES_ENABLED(/home/vagrant/repo/v2-ansible/ansible.cfg) = False
```
##### 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.
-->
Debian 9.1
`Linux dojo 4.9.0-3-amd64 #1 SMP Debian 4.9.30-2+deb9u5 (2017-09-19) x86_64 GNU/Linux`

##### SUMMARY
<!--- Explain the problem briefly -->
The `ec2_vpc_route_table` module crashes on route table creation. The AWS Console shows that the route table is created successfully however.

The error appears to be related to the `tags` attribute not being recognized. The AWS Console shows that the route table is tagged successfully however.

Subsequent runs fail in the same fashion. 

Omitting the `tags` parameter creates an untagged route table, but still crashes the same way.

##### 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.
-->
Create a new route table:
<!--- Paste example playbooks or commands between quotes below -->
```yaml
- name: Create subnet routes
  ec2_vpc_route_table:
    vpc_id: "{{ vpc.vpcs.0.id }}"
    region: "{{ ec2_region }}"
    tags:
      Name: "{{ subnet_name }}"
    subnets:
      - "{{ subnet_name }}"
    routes:
      - dest: "{{ cidr_ip_all }}"
        gateway_id: "{{ igw.internet_gateways.0.internet_gateway_id }}"
```

<!--- You can also paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- What did you expect to happen when running the steps above? -->
Route table is created without errors. This works in `2.4.3`.
##### ACTUAL RESULTS
<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->
MODULE FAILURE
<!--- Paste verbatim command output between quotes below -->
```
The full traceback is:
Traceback (most recent call last):
  File "/tmp/ansible_hbuePJ/ansible_module_ec2_vpc_route_table.py", line 724, in <module>
    main()
  File "/tmp/ansible_hbuePJ/ansible_module_ec2_vpc_route_table.py", line 716, in main
    result = ensure_route_table_present(connection, module)
  File "/tmp/ansible_hbuePJ/ansible_module_ec2_vpc_route_table.py", line 674, in ensure_route_table_present
    associated_subnets = find_subnets(connection, module, vpc_id, subnets)
  File "/tmp/ansible_hbuePJ/ansible_module_ec2_vpc_route_table.py", line 295, in find_subnets
    matching_count = len([1 for s in subnets_by_name if s.tags.get('Name') == name])
AttributeError: 'dict' object has no attribute 'tags'

fatal: [localhost]: FAILED! => {
    "changed": false, 
    "module_stderr": "Traceback (most recent call last):\n  File \"/tmp/ansible_hbuePJ/ansible_module_ec2_vpc_route_table.py\", line 724, in <module>\n    main()\n  File \"/tmp/ansible_hbuePJ/ansible_module_ec2_vpc_route_table.py\", line 716, in main\n    result = ensure_route_table_present(connection, module)\n  File \"/tmp/ansible_hbuePJ/ansible_module_ec2_vpc_route_table.py\", line 674, in ensure_route_table_present\n    associated_subnets = find_subnets(connection, module, vpc_id, subnets)\n  File \"/tmp/ansible_hbuePJ/ansible_module_ec2_vpc_route_table.py\", line 295, in find_subnets\n    matching_count = len([1 for s in subnets_by_name if s.tags.get('Name') == name])\nAttributeError: 'dict' object has no attribute 'tags'\n", 
    "module_stdout": "", 
    "msg": "MODULE FAILURE", 
    "rc": 1
}

```



Discussion:
Files identified in the description:
* [lib/ansible/modules/cloud/amazon/ec2_vpc_route_table.py](https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/cloud/amazon/ec2_vpc_route_table.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 @willthames @wimnat
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
@jrko can you try this out #38707 and give us a feedback? Thanks!
@ZuZuD working for me as well :+1: 

Resolving patch:
diff --git a/lib/ansible/modules/cloud/amazon/ec2_vpc_route_table.py b/lib/ansible/modules/cloud/amazon/ec2_vpc_route_table.py
--- a/lib/ansible/modules/cloud/amazon/ec2_vpc_route_table.py
+++ b/lib/ansible/modules/cloud/amazon/ec2_vpc_route_table.py
@@ -256,7 +256,7 @@ def find_subnets(connection, module, vpc_id, identified_subnets):
     'Name' tag, or a CIDR such as 10.0.0.0/8.
 
     Note that this function is duplicated in other ec2 modules, and should
-    potentially be moved into potentially be moved into a shared module_utils
+    potentially be moved into a shared module_utils
     """
     subnet_ids = []
     subnet_names = []
@@ -294,7 +294,7 @@ def find_subnets(connection, module, vpc_id, identified_subnets):
             module.fail_json_aws(e, msg="Couldn't find subnet with names %s" % subnet_names)
 
         for name in subnet_names:
-            matching_count = len([1 for s in subnets_by_name if s.tags.get('Name') == name])
+            matching_count = len([1 for s in subnets_by_name for t in s.get('Tags', []) if t['Key'] == 'Name' and t['Value'] == name])
             if matching_count == 0:
                 module.fail_json(msg='Subnet named "{0}" does not exist'.format(name))
             elif matching_count > 1:


Test patch:
diff --git a/test/integration/targets/ec2_vpc_route_table/tasks/main.yml b/test/integration/targets/ec2_vpc_route_table/tasks/main.yml
--- a/test/integration/targets/ec2_vpc_route_table/tasks/main.yml
+++ b/test/integration/targets/ec2_vpc_route_table/tasks/main.yml
@@ -25,7 +25,7 @@
       state: present
       tags:
         Public: "{{ item.public|string }}"
-        Name: "{{ item.public|ternary('public', 'private') }}-{{ item.az }}"
+        Name: "{{ (item.public|bool)|ternary('public', 'private') }}-{{ item.az }}"
       <<: *aws_connection_info
     with_items:
       - cidr: 10.228.228.0/24
@@ -337,6 +337,78 @@
       that:
         - check_mode_results.changed
 
+  - name: add subnets by cidr to public route table
+    ec2_vpc_route_table:
+      vpc_id: "{{ vpc.vpc.id }}"
+      routes:
+      - dest: 0.0.0.0/0
+        gateway_id: igw
+      subnets: "{{ vpc_subnets|json_query('subnets[?tags.Public == `True`].cidr_block') }}"
+      lookup: id
+      route_table_id: "{{ create_public_table.route_table.id }}"
+      <<: *aws_connection_info
+    register: add_subnets_cidr
+
+  - name: assert route table contains subnets added by cidr
+    assert:
+      that:
+        - add_subnets_cidr.changed
+        - add_subnets_cidr.route_table.associations|length == 2
+
+  - name: purge subnets added by cidr
+    ec2_vpc_route_table:
+      vpc_id: "{{ vpc.vpc.id }}"
+      routes:
+      - dest: 0.0.0.0/0
+        gateway_id: igw
+      subnets: []
+      lookup: id
+      route_table_id: "{{ create_public_table.route_table.id }}"
+      <<: *aws_connection_info
+    register: purge_subnets_cidr
+
+  - name: assert purge subnets added by cidr worked
+    assert:
+      that:
+        - purge_subnets_cidr.changed
+        - purge_subnets_cidr.route_table.associations|length == 0
+
+  - name: add subnets by name to public route table
+    ec2_vpc_route_table:
+      vpc_id: "{{ vpc.vpc.id }}"
+      routes:
+      - dest: 0.0.0.0/0
+        gateway_id: igw
+      subnets: "{{ vpc_subnets|json_query('subnets[?tags.Public == `True`].tags.Name') }}"
+      lookup: id
+      route_table_id: "{{ create_public_table.route_table.id }}"
+      <<: *aws_connection_info
+    register: add_subnets_name
+
+  - name: assert route table contains subnets added by name
+    assert:
+      that:
+        - add_subnets_name.changed
+        - add_subnets_name.route_table.associations|length == 2
+
+  - name: purge subnets added by name
+    ec2_vpc_route_table:
+      vpc_id: "{{ vpc.vpc.id }}"
+      routes:
+      - dest: 0.0.0.0/0
+        gateway_id: igw
+      subnets: []
+      lookup: id
+      route_table_id: "{{ create_public_table.route_table.id }}"
+      <<: *aws_connection_info
+    register: purge_subnets_name
+
+  - name: assert purge subnets added by name worked
+    assert:
+      that:
+        - purge_subnets_name.changed
+        - purge_subnets_name.route_table.associations|length == 0
+
   - name: purge routes
     ec2_vpc_route_table:
       vpc_id: "{{ vpc.vpc.id }}"