Repository: getmoto/moto

Issue:
Unable to create a subnet from a secondary VPC CIDR
## Background

I was attempting to create a Subnet from a secondary VPC CIDR and was getting a subnet validation error.

It appears that this is because `create_subnet` only validates the CIDR is a subset of the VPC primary CIDR (https://github.com/spulec/moto/blob/master/moto/ec2/models.py#L3433-L3435). Instead I believe this should be validated against all CIDRs in the VPC `cidr block association sets`

## Observed Behavior

To reproduce you can run the below code (I tested with moto@1.3.16 and boto3@1.14.40 in python3.7):

```python
import boto3
from moto import mock_sts, mock_ec2

@mock_sts
@mock_ec2
def main():
    client = boto3.client("ec2", region_name="us-west-2")

    vpc = client.create_vpc(CidrBlock="10.0.0.0/16")

    # Succeeds
    subnet1 = client.create_subnet(
        AvailabilityZone="us-west-2a",
        CidrBlock="10.0.0.0/24",
        VpcId=vpc["Vpc"]["VpcId"],
    )

   print(subnet1)

   client.associate_vpc_cidr_block(
        CidrBlock="10.1.0.0/16",
        VpcId=vpc["Vpc"]["VpcId"],
    )

    # Fails
    subnet2 = client.create_subnet(
        AvailabilityZone="us-west-2a",
        CidrBlock="10.1.0.0/24",
        VpcId=vpc["Vpc"]["VpcId"],
    )

    print(subnet2)

if __name__ == '__main__':
    main()
```

## Output / Traceback

```
{'Subnet': {'AvailabilityZone': 'us-west-2a', 'AvailabilityZoneId': 'usw2-az2', 'AvailableIpAddressCount': 251, 'CidrBlock': '10.0.0.0/24', 'DefaultForAz': False, 'MapPublicIpOnLaunch': False, 'State': 'pending', 'SubnetId': 'subnet-89f4d757', 'VpcId': 'vpc-5c9431df', 'OwnerId': '111122223333', 'AssignIpv6AddressOnCreation': False, 'Ipv6CidrBlockAssociationSet': [], 'SubnetArn': 'arn:aws:ec2:us-west-2:111122223333:subnet/subnet-89f4d757'}, 'ResponseMetadata': {'RequestId': '7a62c49f-347e-4fc4-9331-6e8eEXAMPLE', 'HTTPStatusCode': 200, 'HTTPHeaders': {'server': 'amazon.com'}, 'RetryAttempts': 0}}
Traceback (most recent call last):
  File "test.py", line 35, in <module>
    main()
  File "/path/to/.venv/lib/python3.7/site-packages/moto/core/models.py", line 100, in wrapper
    result = func(*args, **kwargs)
  File "/path/to/.venv/lib/python3.7/site-packages/moto/core/models.py", line 100, in wrapper
    result = func(*args, **kwargs)
  File "test.py", line 29, in main
    VpcId=vpc["Vpc"]["VpcId"],
  File "/path/to/.venv/lib/python3.7/site-packages/botocore/client.py", line 316, in _api_call
    return self._make_api_call(operation_name, kwargs)
  File "/path/to/.venv/lib/python3.7/site-packages/botocore/client.py", line 635, in _make_api_call
    raise error_class(parsed_response, operation_name)
botocore.exceptions.ClientError: An error occurred (InvalidSubnet.Range) when calling the CreateSubnet operation: The CIDR '10.1.0.0/24' is invalid.
```


## Expected Behavior

Resource `subnet2` should pass validation and be successfully created in the backend.



Discussion:
Thanks for raising this, and providing a test case @davidaah - that's always helpful. Marking it as a bug.

Resolving patch:
diff --git a/moto/ec2/models.py b/moto/ec2/models.py
--- a/moto/ec2/models.py
+++ b/moto/ec2/models.py
@@ -3465,19 +3465,30 @@ def create_subnet(
         vpc = self.get_vpc(
             vpc_id
         )  # Validate VPC exists and the supplied CIDR block is a subnet of the VPC's
-        vpc_cidr_block = ipaddress.IPv4Network(
-            six.text_type(vpc.cidr_block), strict=False
-        )
+        vpc_cidr_blocks = [
+            ipaddress.IPv4Network(
+                six.text_type(cidr_block_association["cidr_block"]), strict=False
+            )
+            for cidr_block_association in vpc.get_cidr_block_association_set()
+        ]
         try:
             subnet_cidr_block = ipaddress.IPv4Network(
                 six.text_type(cidr_block), strict=False
             )
         except ValueError:
             raise InvalidCIDRBlockParameterError(cidr_block)
-        if not (
-            vpc_cidr_block.network_address <= subnet_cidr_block.network_address
-            and vpc_cidr_block.broadcast_address >= subnet_cidr_block.broadcast_address
-        ):
+
+        subnet_in_vpc_cidr_range = False
+        for vpc_cidr_block in vpc_cidr_blocks:
+            if (
+                vpc_cidr_block.network_address <= subnet_cidr_block.network_address
+                and vpc_cidr_block.broadcast_address
+                >= subnet_cidr_block.broadcast_address
+            ):
+                subnet_in_vpc_cidr_range = True
+                break
+
+        if not subnet_in_vpc_cidr_range:
             raise InvalidSubnetRangeError(cidr_block)
 
         for subnet in self.get_all_subnets(filters={"vpc-id": vpc_id}):


Test patch:
diff --git a/tests/test_ec2/test_subnets.py b/tests/test_ec2/test_subnets.py
--- a/tests/test_ec2/test_subnets.py
+++ b/tests/test_ec2/test_subnets.py
@@ -417,6 +417,24 @@ def test_create_subnet_with_invalid_cidr_range():
     )
 
 
+@mock_ec2
+def test_create_subnet_with_invalid_cidr_range_multiple_vpc_cidr_blocks():
+    ec2 = boto3.resource("ec2", region_name="us-west-1")
+
+    vpc = ec2.create_vpc(CidrBlock="10.0.0.0/16")
+    ec2.meta.client.associate_vpc_cidr_block(CidrBlock="10.1.0.0/16", VpcId=vpc.id)
+    vpc.reload()
+    vpc.is_default.shouldnt.be.ok
+
+    subnet_cidr_block = "10.2.0.0/20"
+    with assert_raises(ClientError) as ex:
+        subnet = ec2.create_subnet(VpcId=vpc.id, CidrBlock=subnet_cidr_block)
+    str(ex.exception).should.equal(
+        "An error occurred (InvalidSubnet.Range) when calling the CreateSubnet "
+        "operation: The CIDR '{}' is invalid.".format(subnet_cidr_block)
+    )
+
+
 @mock_ec2
 def test_create_subnet_with_invalid_cidr_block_parameter():
     ec2 = boto3.resource("ec2", region_name="us-west-1")
@@ -436,6 +454,46 @@ def test_create_subnet_with_invalid_cidr_block_parameter():
     )
 
 
+@mock_ec2
+def test_create_subnets_with_multiple_vpc_cidr_blocks():
+    ec2 = boto3.resource("ec2", region_name="us-west-1")
+    client = boto3.client("ec2", region_name="us-west-1")
+
+    vpc = ec2.create_vpc(CidrBlock="10.0.0.0/16")
+    ec2.meta.client.associate_vpc_cidr_block(CidrBlock="10.1.0.0/16", VpcId=vpc.id)
+    vpc.reload()
+    vpc.is_default.shouldnt.be.ok
+
+    subnet_cidr_block_primary = "10.0.0.0/24"
+    subnet_primary = ec2.create_subnet(
+        VpcId=vpc.id, CidrBlock=subnet_cidr_block_primary
+    )
+
+    subnet_cidr_block_secondary = "10.1.0.0/24"
+    subnet_secondary = ec2.create_subnet(
+        VpcId=vpc.id, CidrBlock=subnet_cidr_block_secondary
+    )
+
+    subnets = client.describe_subnets(
+        SubnetIds=[subnet_primary.id, subnet_secondary.id]
+    )["Subnets"]
+    subnets.should.have.length_of(2)
+
+    for subnet in subnets:
+        subnet.should.have.key("AvailabilityZone")
+        subnet.should.have.key("AvailabilityZoneId")
+        subnet.should.have.key("AvailableIpAddressCount")
+        subnet.should.have.key("CidrBlock")
+        subnet.should.have.key("State")
+        subnet.should.have.key("SubnetId")
+        subnet.should.have.key("VpcId")
+        subnet.shouldnt.have.key("Tags")
+        subnet.should.have.key("DefaultForAz").which.should.equal(False)
+        subnet.should.have.key("MapPublicIpOnLaunch").which.should.equal(False)
+        subnet.should.have.key("OwnerId")
+        subnet.should.have.key("AssignIpv6AddressOnCreation").which.should.equal(False)
+
+
 @mock_ec2
 def test_create_subnets_with_overlapping_cidr_blocks():
     ec2 = boto3.resource("ec2", region_name="us-west-1")