Repository: ansible/ansible

Issue:
ec2_group check-mode fails when tags are set: 'NoneType' object has no attribute 'get'
<!---
Verify first that your issue/request is not already reported on GitHub.
Also test if the latest release, and devel branch are affected too.
-->

##### ISSUE TYPE
<!--- Pick one below and delete the rest -->
 - Bug Report

##### COMPONENT NAME
ec2_group module

##### ANSIBLE VERSION
<!--- Paste verbatim output from "ansible --version" between quotes below -->
Note: Installed latest version from python-pip
```
ansible 2.4.3.0
  config file = /etc/ansible/ansible.cfg
  configured module search path = [u'/home/x/.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.12 (default, Dec  4 2017, 14:50:18) [GCC 5.4.0 20160609]
```

##### CONFIGURATION
N/A

##### OS / ENVIRONMENT
```
Linux ubuntu 4.4.0-112-generic #135-Ubuntu SMP Fri Jan 19 11:48:36 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux
```
##### SUMMARY
Unable to run successful --check against ec2_group when "tags" attribute is specified.
ec2_group does function when --check is not set.

##### STEPS TO REPRODUCE

Run the following with --check

<!--- Paste example playbooks or commands between quotes below -->
```yaml
# export AWS_PROFILE=local
# export AWS_REGION=us-east-1
# ansible-playbook playbook.yml -vv --check
#
- hosts: localhost
  connection: local
  gather_facts: false
  tasks:
  - name: "Locate VPC for use"
    ec2_vpc_net_facts:
      filters:
        # for some reason boolean true/false doesn't work
        is_default: "true"
    register: vpc_list
    check_mode: no

  - debug:
      verbosity: 1
      var: vpc_list

  - name: "Create Security Group"
    ec2_group:
      name: "my-test-sg"
      description: "Test Security Group created for the purpose of testing ec2_group"
      vpc_id: "{{ vpc_list.vpcs[0].id }}"
      state: present
      tags:
        tag1: atag
        tag2: btag

```

<!--- You can also paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- What did you expect to happen when running the steps above? -->
Expecting check-mode to complete without errors

##### ACTUAL RESULTS
<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->

<!--- Paste verbatim command output between quotes below -->
```
ansible-playbook 2.4.3.0
  config file = /etc/ansible/ansible.cfg
  configured module search path = [u'/home/x/.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-playbook
  python version = 2.7.12 (default, Dec  4 2017, 14:50:18) [GCC 5.4.0 20160609]
Using /etc/ansible/ansible.cfg as config file
setting up inventory plugins
Parsed /etc/ansible/hosts inventory source with ini plugin
Loading callback plugin default of type stdout, v2.0 from /usr/local/lib/python2.7/dist-packages/ansible/plugins/callback/default.pyc

PLAYBOOK: playbook.yml ************************************************************************************************************************************************************
1 plays in playbook.yml

PLAY [localhost] ******************************************************************************************************************************************************************
META: ran handlers

TASK [Locate VPC for use] *********************************************************************************************************************************************************
task path: /home/x/Temp/playbook.yml:9
Using module file /usr/local/lib/python2.7/dist-packages/ansible/modules/cloud/amazon/ec2_vpc_net_facts.py
<127.0.0.1> ESTABLISH LOCAL CONNECTION FOR USER: x
<127.0.0.1> EXEC /bin/sh -c 'echo ~ && sleep 0'
<127.0.0.1> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /home/x/.ansible/tmp/ansible-tmp-1519076271.41-142040860125374 `" && echo ansible-tmp-1519076271.41-142040860125374="` echo /home/x/.ansible/tmp/ansible-tmp-1519076271.41-142040860125374 `" ) && sleep 0'
<127.0.0.1> PUT /tmp/tmpFkhYjy TO /home/x/.ansible/tmp/ansible-tmp-1519076271.41-142040860125374/ec2_vpc_net_facts.py
<127.0.0.1> EXEC /bin/sh -c 'chmod u+x /home/x/.ansible/tmp/ansible-tmp-1519076271.41-142040860125374/ /home/x/.ansible/tmp/ansible-tmp-1519076271.41-142040860125374/ec2_vpc_net_facts.py && sleep 0'
<127.0.0.1> EXEC /bin/sh -c '/usr/bin/python /home/x/.ansible/tmp/ansible-tmp-1519076271.41-142040860125374/ec2_vpc_net_facts.py; rm -rf "/home/x/.ansible/tmp/ansible-tmp-1519076271.41-142040860125374/" > /dev/null 2>&1 && sleep 0'
ok: [localhost] => {
    "changed": false,
    "invocation": {
        "module_args": {
            "aws_access_key": null,
            "aws_secret_key": null,
            "ec2_url": null,
            "filters": {
                "is_default": "true"
            },
            "profile": null,
            "region": null,
            "security_token": null,
            "validate_certs": true
        }
    },
    "vpcs": [
        {
            "cidr_block": "172.31.0.0/16",
            "classic_link_enabled": null,
            "dhcp_options_id": "dopt-cebd4dab",
            "id": "vpc-34041251",
            "instance_tenancy": "default",
            "is_default": true,
            "state": "available",
            "tags": {
                "Name": "AWS_default_vpc"
            }
        }
    ]
}

TASK [debug] **********************************************************************************************************************************************************************
task path: /home/x/Temp/playbook.yml:17
ok: [localhost] => {
    "vpc_list": {
        "changed": false,
        "failed": false,
        "vpcs": [
            {
                "cidr_block": "172.31.0.0/16",
                "classic_link_enabled": null,
                "dhcp_options_id": "dopt-cebd4dab",
                "id": "vpc-34041251",
                "instance_tenancy": "default",
                "is_default": true,
                "state": "available",
                "tags": {
                    "Name": "AWS_default_vpc"
                }
            }
        ]
    }
}

TASK [Create Security Group] ******************************************************************************************************************************************************
task path: /home/x/Temp/playbook.yml:21
Using module file /usr/local/lib/python2.7/dist-packages/ansible/modules/cloud/amazon/ec2_group.py
<127.0.0.1> ESTABLISH LOCAL CONNECTION FOR USER: x
<127.0.0.1> EXEC /bin/sh -c 'echo ~ && sleep 0'
<127.0.0.1> EXEC /bin/sh -c '( umask 77 && mkdir -p "` echo /home/x/.ansible/tmp/ansible-tmp-1519076272.09-150370328758812 `" && echo ansible-tmp-1519076272.09-150370328758812="` echo /home/x/.ansible/tmp/ansible-tmp-1519076272.09-150370328758812 `" ) && sleep 0'
<127.0.0.1> PUT /tmp/tmpDwQB3A TO /home/x/.ansible/tmp/ansible-tmp-1519076272.09-150370328758812/ec2_group.py
<127.0.0.1> EXEC /bin/sh -c 'chmod u+x /home/x/.ansible/tmp/ansible-tmp-1519076272.09-150370328758812/ /home/x/.ansible/tmp/ansible-tmp-1519076272.09-150370328758812/ec2_group.py && sleep 0'
<127.0.0.1> EXEC /bin/sh -c '/usr/bin/python /home/x/.ansible/tmp/ansible-tmp-1519076272.09-150370328758812/ec2_group.py; rm -rf "/home/x/.ansible/tmp/ansible-tmp-1519076272.09-150370328758812/" > /dev/null 2>&1 && sleep 0'
The full traceback is:
Traceback (most recent call last):
  File "/tmp/ansible_C5u5kq/ansible_module_ec2_group.py", line 899, in <module>
    main()
  File "/tmp/ansible_C5u5kq/ansible_module_ec2_group.py", line 711, in main
    current_tags = boto3_tag_list_to_ansible_dict(group.get('Tags', []))
AttributeError: 'NoneType' object has no attribute 'get'
fatal: [localhost]: FAILED! => {
    "changed": false,
    "module_stderr": "Traceback (most recent call last):\n  File \"/tmp/ansible_C5u5kq/ansible_module_ec2_group.py\", line 899, in <module>\n    main()\n  File \"/tmp/ansible_C5u5kq/ansible_module_ec2_group.py\", line 711, in main\n    current_tags = boto3_tag_list_to_ansible_dict(group.get('Tags', []))\nAttributeError: 'NoneType' object has no attribute 'get'\n",
    "module_stdout": "",
    "msg": "MODULE FAILURE",
    "rc": 0
}
        to retry, use: --limit @/home/x/Temp/playbook.retry

PLAY RECAP ************************************************************************************************************************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=1

```



Discussion:
I have not attempted to verify against the latest devel branch. Maybe I'll do that after I get home from work.
cc @adq @willthames
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
If it's possible to reproduce on `devel` please update here. 

needs_info
Will do, haven't had time yet so far. I'll try to get it in the next few days.

Resolving patch:
diff --git a/lib/ansible/modules/cloud/amazon/ec2_group.py b/lib/ansible/modules/cloud/amazon/ec2_group.py
--- a/lib/ansible/modules/cloud/amazon/ec2_group.py
+++ b/lib/ansible/modules/cloud/amazon/ec2_group.py
@@ -789,22 +789,24 @@ def main():
 
             changed = True
 
-        if tags is not None:
+        if tags is not None and group is not None:
             current_tags = boto3_tag_list_to_ansible_dict(group.get('Tags', []))
             tags_need_modify, tags_to_delete = compare_aws_tags(current_tags, tags, purge_tags)
             if tags_to_delete:
-                try:
-                    client.delete_tags(Resources=[group['GroupId']], Tags=[{'Key': tag} for tag in tags_to_delete])
-                except botocore.exceptions.ClientError as e:
-                    module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))
+                if not module.check_mode:
+                    try:
+                        client.delete_tags(Resources=[group['GroupId']], Tags=[{'Key': tag} for tag in tags_to_delete])
+                    except botocore.exceptions.ClientError as e:
+                        module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))
                 changed = True
 
             # Add/update tags
             if tags_need_modify:
-                try:
-                    client.create_tags(Resources=[group['GroupId']], Tags=ansible_dict_to_boto3_tag_list(tags_need_modify))
-                except botocore.exceptions.ClientError as e:
-                    module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))
+                if not module.check_mode:
+                    try:
+                        client.create_tags(Resources=[group['GroupId']], Tags=ansible_dict_to_boto3_tag_list(tags_need_modify))
+                    except botocore.exceptions.ClientError as e:
+                        module.fail_json(msg=e.message, exception=traceback.format_exc(), **camel_dict_to_snake_dict(e.response))
                 changed = True
 
     else:


Test patch: