Repository: ansible/ansible

Issue:
Creating a security group with none rules_egress doesn't create default all-out rule
##### ISSUE TYPE
 - Bug Report

##### COMPONENT NAME
ec2_group

##### ANSIBLE VERSION
```
ansible 2.5.0
  config file = /Users/baqer/deployment/ansible.cfg
  configured module search path = [u'/Users/baqer/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /Users/baqer/Library/Python/2.7/lib/python/site-packages/ansible
  executable location = /usr/local/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
ANSIBLE_SSH_ARGS(/Users/baqer/deployment/ansible.cfg) = -o ControlMaster=auto -o ControlPersist=60s -o ControlPath=/tmp/ansible-s
HOST_KEY_CHECKING(/Users/baqer/deployment/ansible.cfg) = False

##### OS / ENVIRONMENT
N/A

##### SUMMARY
If none `rules_egress` is specified, a default all-out rule is **not** assumed. The [docs](http://docs.ansible.com/ansible/latest/ec2_group_module.html) indicates the contrary.

##### STEPS TO REPRODUCE
1. Create a simple playbook that creates an EC2 security group
```yaml
- name: Create a security group for EC2 instances
  ec2_group:
    name: "test"
    description: "Test"

    region: "ap-southeast-2"
    rules:
      - proto: tcp
        from_port: 22
        to_port: 22
        cidr_ip: 0.0.0.0/0
```

2. Run the following command:
```
$ aws ec2 describe-security-groups --group-name test --region ap-southeast-2
```

##### EXPECTED RESULTS
```
{
    "SecurityGroups": [
        {
            "IpPermissionsEgress": [
                {
                    "PrefixListIds": [],
                    "FromPort": 0,
                    "IpRanges": [
                        {
                            "CidrIp": "0.0.0.0/0"
                        }
                    ],
                    "ToPort": 65535,
                    "IpProtocol": "tcp",
                    "UserIdGroupPairs": [],
                    "Ipv6Ranges": []
                }
            ],
            "Description": "Test",
            "IpPermissions": [
                {
                    "PrefixListIds": [],
                    "FromPort": 22,
                    "IpRanges": [
                        {
                            "CidrIp": "0.0.0.0/0"
                        }
                    ],
                    "ToPort": 22,
                    "IpProtocol": "tcp",
                    "UserIdGroupPairs": [],
                    "Ipv6Ranges": []
                }
            ],
            "GroupName": "test",
            "VpcId": "REDACTED",
            "OwnerId": "REDACTED",
            "GroupId": "REDACTED"
        }
    ]
}
```

##### ACTUAL RESULTS
```
{
    "SecurityGroups": [
        {
            "IpPermissionsEgress": [],
            "Description": "Test",
            "IpPermissions": [
                {
                    "PrefixListIds": [],
                    "FromPort": 22,
                    "IpRanges": [
                        {
                            "CidrIp": "0.0.0.0/0"
                        }
                    ],
                    "ToPort": 22,
                    "IpProtocol": "tcp",
                    "UserIdGroupPairs": [],
                    "Ipv6Ranges": []
                }
            ],
            "GroupName": "test",
            "VpcId": "REDACTED",
            "OwnerId": "REDACTED",
            "GroupId": "REDACTED"
        }
    ]
}
```



Discussion:
Files identified in the description:
* [lib/ansible/modules/cloud/amazon/ec2_group.py](https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/cloud/amazon/ec2_group.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 @adq @willthames
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
Thanks for raising this with all this info - I'll try to repro this on 2.5. 
It looks like this group is being created outside a VPC (no VPC ID in the task) - my test here:

```
- hosts: localhost
  connection: local
  gather_facts: false
  tasks:
    - ec2_group:
        name: foo-test-group
        description: test
        vpc_id: vpc-be10f2d9
        rules:
        - proto: tcp 
          from_port: 22
          to_port: 22
          cidr_ip: 0.0.0.0/0
    - pause: minutes=2
    - ec2_group:
        state: absent
        name: foo-test-group
        vpc_id: vpc-be10f2d9
```

Results in a group with the default egress rule:

```
        {
            "IpPermissionsEgress": [
                {
                    "IpProtocol": "-1", 
                    "PrefixListIds": [], 
                    "IpRanges": [
                        {
                            "CidrIp": "0.0.0.0/0"
                        }
                    ], 
                    "UserIdGroupPairs": [], 
                    "Ipv6Ranges": []
                }
            ], 
            "Description": "test", 
            "IpPermissions": [
                {
                    "PrefixListIds": [], 
                    "FromPort": 22, 
                    "IpRanges": [
                        {
                            "CidrIp": "0.0.0.0/0"
                        }
                    ], 
                    "ToPort": 22, 
                    "IpProtocol": "tcp", 
                    "UserIdGroupPairs": [], 
                    "Ipv6Ranges": []
                }
            ], 
            "GroupName": "foo-test-group", 
            "VpcId": "vpc-be10f2d9", 
            "OwnerId": "REDACTED", 
            "GroupId": "sg-5dda2123"
        }, 
```

My best guess is that it's to do with your default VPC or account EC2 Classic support. Can you run `aws ec2 describe-account-attributes` and look at the value of `supported-platforms`? It will either just say "VPC" or "VPC, EC2-Classic".
needs_info
It only says "VPC":
```
        {
            "AttributeName": "supported-platforms",
            "AttributeValues": [
                {
                    "AttributeValue": "VPC"
                }
            ]
        }
```

By the way, this was working on Ansible 2.4. (specifying none rules_egress assumed a default all-out rule)
We should have a test case for this, not sure why it's not working this time around. It might be related to #34626, which fixed the other way around (groups always getting the default egress rule even when specifying egress rules). I'll take a look.
Ok, we don't have good tests for this and the behaviour definitely looks buggy!

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
@@ -941,7 +941,7 @@ def main():
                     # If rule already exists, don't later delete it
                     changed, ip_permission = authorize_ip("out", changed, client, group, groupRules, ipv6,
                                                           ip_permission, module, rule, "ipv6")
-        elif vpc_id is not None:
+        elif 'VpcId' in group:
             # when no egress rules are specified and we're in a VPC,
             # we add in a default allow all out rule, which was the
             # default behavior before egress rules were added


Test patch:
diff --git a/test/integration/targets/ec2_group/tasks/main.yml b/test/integration/targets/ec2_group/tasks/main.yml
--- a/test/integration/targets/ec2_group/tasks/main.yml
+++ b/test/integration/targets/ec2_group/tasks/main.yml
@@ -422,6 +422,8 @@
         that:
         - 'result.changed'
         - 'result.group_id.startswith("sg-")'
+        - 'result.ip_permissions|length == 1'
+        - 'result.ip_permissions_egress|length == 1'
 
     # ============================================================
     - name: add same rule to the existing group  (expected changed=false)
@@ -464,6 +466,7 @@
           - result.ip_permissions|length == 2
           - result.ip_permissions[0].user_id_group_pairs or
             result.ip_permissions[1].user_id_group_pairs
+          - 'result.ip_permissions_egress[0].ip_protocol == "-1"'
 
     # ============================================================
     - name: test ip rules convert port numbers from string to int (expected changed=true)
@@ -489,6 +492,9 @@
         that:
            - 'result.changed'
            - 'result.group_id.startswith("sg-")'
+           - 'result.ip_permissions|length == 1'
+           - 'result.ip_permissions_egress[0].ip_protocol == "tcp"'
+
 
     # ============================================================
     - name: test group rules convert port numbers from string to int (expected changed=true)