Repository: ansible/ansible

Issue:
null value in ec2_group causes TypeError
<!---
Verify first that your issue/request is not already reported on GitHub.
Also test if the latest release, and master branch are affected too.
-->
null `group_id` in `rules` list causes `TypeError`

##### ISSUE TYPE
<!--- Pick one below and delete the rest: -->
 - Bug Report

##### COMPONENT NAME
<!--- Name of the module/plugin/task/feature -->
ec2_group

##### ANSIBLE VERSION
<!--- Paste verbatim output from “ansible --version” between quotes below -->
```
ansible 2.3.1.0
  config file = /Users/tom/repos/aws-codecommit/ansible-tower/ansible.cfg
  configured module search path = Default w/o overrides
  python version = 2.7.13 (default, Apr  4 2017, 08:47:57) [GCC 4.2.1 Compatible Apple LLVM 8.1.0 (clang-802.0.38)]
```

##### CONFIGURATION
<!---
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.
-->
Using latest devel version of ec2_group

https://github.com/ansible/ansible/blob/6b76bc924fdc7b1fbce71f1fb161e9b6c90b5058/lib/ansible/modules/cloud/amazon/ec2_group.py

##### SUMMARY
<!--- Explain the problem briefly -->
If you specify a empty (null) value for `group_id` inside `rules`. A `TypeError` will occur.

No other modules tested behave the same way and apply sensible defaults when presented with null values.

##### 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 -->
```yaml
- name: create security group
  ec2_group:
    description: foo
    name: bar
    purge_rules: true
    purge_rules_egress: true
    region: eu-central-1
    rules:
      - proto: tcp
        from_port: 22
        to_port: 22
        cidr_ip:
          - 0.0.0.0/0
        group_id:
    rules_egress:
      - []
    state: present
    vpc_id: vpc-123456a
```

<!--- You can also paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- What did you expect to happen when running the steps above? -->
A security group is created.

##### ACTUAL RESULTS
<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->

<!--- Paste verbatim command output between quotes below -->
```
The full traceback is:
Traceback (most recent call last):
  File "/var/folders/9w/w2f_52y12pq86rn0glnxdgs40000gn/T/ansible_QMC0WD/ansible_module_ec2_group.py", line 632, in <module>
    main()
  File "/var/folders/9w/w2f_52y12pq86rn0glnxdgs40000gn/T/ansible_QMC0WD/ansible_module_ec2_group.py", line 505, in main
    group_id, ip, target_group_created = get_target_from_rule(module, ec2, rule, name, group, groups, vpc_id)
  File "/var/folders/9w/w2f_52y12pq86rn0glnxdgs40000gn/T/ansible_QMC0WD/ansible_module_ec2_group.py", line 280, in get_target_from_rule
    elif 'group_id' in rule and 'group_id' is not None and re.match(FOREIGN_SECURITY_GROUP_REGEX, rule['group_id']):
  File "/usr/local/Cellar/python/2.7.13/Frameworks/Python.framework/Versions/2.7/lib/python2.7/re.py", line 141, in match
    return _compile(pattern, flags).match(string)
TypeError: expected string or buffer

fatal: [localhost]: FAILED! => {
    "changed": false,
    "failed": true,
    "module_stderr": "Traceback (most recent call last):\n  File \"/var/folders/9w/w2f_52y12pq86rn0glnxdgs40000gn/T/ansible_QMC0WD/ansible_module_ec2_group.py\", line 632, in <module>\n    main()\n  File \"/var/folders/9w/w2f_52y12pq86rn0glnxdgs40000gn/T/ansible_QMC0WD/ansible_module_ec2_group.py\", line 505, in main\n    group_id, ip, tar
get_group_created = get_target_from_rule(module, ec2, rule, name, group, groups, vpc_id)\n  File \"/var/folders/9w/w2f_52y12pq86rn0glnxdgs40000gn/T/ansible_QMC0WD/ansible_module_ec2_group.py\", line 280, in get_target_from_rule\n    elif 'group_id' in rule and 'group_id' is not None and re.match(FOREIGN_SECURITY_GROUP_REGEX, rule['group_id
']):\n  File \"/usr/local/Cellar/python/2.7.13/Frameworks/Python.framework/Versions/2.7/lib/python2.7/re.py\", line 141, in match\n    return _compile(pattern, flags).match(string)\nTypeError: expected string or buffer\n",
    "module_stdout": "",
    "msg": "MODULE FAILURE",
    "rc": 1
}
```

By removing the empty `group_id:` line from the task, the error goes away.

Line `280` should not try and run the regex check if `group_id` is null?


Discussion:

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
@@ -277,7 +277,7 @@ def get_target_from_rule(module, ec2, rule, name, group, groups, vpc_id):
         module.fail_json(msg="Specify group_name OR cidr_ip, not both")
     elif 'group_id' in rule and 'group_name' in rule:
         module.fail_json(msg="Specify group_id OR group_name, not both")
-    elif 'group_id' in rule and re.match(FOREIGN_SECURITY_GROUP_REGEX, rule['group_id']):
+    elif rule.get('group_id') and re.match(FOREIGN_SECURITY_GROUP_REGEX, rule['group_id']):
         # this is a foreign Security Group. Since you can't fetch it you must create an instance of it
         owner_id, group_id, group_name = re.match(FOREIGN_SECURITY_GROUP_REGEX, rule['group_id']).groups()
         group_instance = SecurityGroup(owner_id=owner_id, name=group_name, id=group_id)


Test patch: