Repository: getmoto/moto

Issue:
create_subnet AvailableIpAddressCount always 251
create_subnet AvailableIpAddressCount always 251
```
response = client.create_vpc(
    CidrBlock='10.0.0.0/16'
 )
vpc_id = response["Vpc"]["VpcId"]

# Create small subnet in VPC
small_subnet = client.create_subnet(CidrBlock='10.0.1.0/27', VpcId=vpc_id)
print(small_subnet)
```

Result

```
{'Subnet': {'AvailabilityZone': 'us-east-1a', 'AvailableIpAddressCount': 251, 'CidrBlock': '10.0.1.0/27', 'State': 'pending', 'SubnetId': 'subnet-f77b31ee', 'VpcId': 'vpc-65214079', 'Tags': []}, 'ResponseMetadata': {'RequestId': '7a62c49f-347e-4fc4-9331-6e8eEXAMPLE', 'HTTPStatusCode': 200, 'HTTPHeaders': {'server': 'amazon.com'}, 'RetryAttempts': 0}}
```


Discussion:
I have also tried running instances in these subnets and the AvailableIpAddressCount doesn't seem to decrease

Resolving patch:
diff --git a/moto/ec2/models.py b/moto/ec2/models.py
--- a/moto/ec2/models.py
+++ b/moto/ec2/models.py
@@ -214,6 +214,7 @@ def __init__(
         ec2_backend,
         subnet,
         private_ip_address,
+        private_ip_addresses=None,
         device_index=0,
         public_ip_auto_assign=True,
         group_ids=None,
@@ -223,6 +224,7 @@ def __init__(
         self.id = random_eni_id()
         self.device_index = device_index
         self.private_ip_address = private_ip_address or random_private_ip()
+        self.private_ip_addresses = private_ip_addresses
         self.subnet = subnet
         self.instance = None
         self.attachment_id = None
@@ -341,12 +343,19 @@ def __init__(self):
         super(NetworkInterfaceBackend, self).__init__()
 
     def create_network_interface(
-        self, subnet, private_ip_address, group_ids=None, description=None, **kwargs
+        self,
+        subnet,
+        private_ip_address,
+        private_ip_addresses=None,
+        group_ids=None,
+        description=None,
+        **kwargs
     ):
         eni = NetworkInterface(
             self,
             subnet,
             private_ip_address,
+            private_ip_addresses,
             group_ids=group_ids,
             description=description,
             **kwargs
@@ -2819,6 +2828,9 @@ def __init__(
         self.vpc_id = vpc_id
         self.cidr_block = cidr_block
         self.cidr = ipaddress.IPv4Network(six.text_type(self.cidr_block), strict=False)
+        self._available_ip_addresses = (
+            ipaddress.IPv4Network(six.text_type(self.cidr_block)).num_addresses - 5
+        )
         self._availability_zone = availability_zone
         self.default_for_az = default_for_az
         self.map_public_ip_on_launch = map_public_ip_on_launch
@@ -2854,6 +2866,21 @@ def create_from_cloudformation_json(
 
         return subnet
 
+    @property
+    def available_ip_addresses(self):
+        enis = [
+            eni
+            for eni in self.ec2_backend.get_all_network_interfaces()
+            if eni.subnet.id == self.id
+        ]
+        addresses_taken = [
+            eni.private_ip_address for eni in enis if eni.private_ip_address
+        ]
+        for eni in enis:
+            if eni.private_ip_addresses:
+                addresses_taken.extend(eni.private_ip_addresses)
+        return str(self._available_ip_addresses - len(addresses_taken))
+
     @property
     def availability_zone(self):
         return self._availability_zone.name
diff --git a/moto/ec2/responses/elastic_network_interfaces.py b/moto/ec2/responses/elastic_network_interfaces.py
--- a/moto/ec2/responses/elastic_network_interfaces.py
+++ b/moto/ec2/responses/elastic_network_interfaces.py
@@ -7,12 +7,13 @@ class ElasticNetworkInterfaces(BaseResponse):
     def create_network_interface(self):
         subnet_id = self._get_param("SubnetId")
         private_ip_address = self._get_param("PrivateIpAddress")
+        private_ip_addresses = self._get_multi_param("PrivateIpAddresses")
         groups = self._get_multi_param("SecurityGroupId")
         subnet = self.ec2_backend.get_subnet(subnet_id)
         description = self._get_param("Description")
         if self.is_not_dryrun("CreateNetworkInterface"):
             eni = self.ec2_backend.create_network_interface(
-                subnet, private_ip_address, groups, description
+                subnet, private_ip_address, private_ip_addresses, groups, description
             )
             template = self.response_template(CREATE_NETWORK_INTERFACE_RESPONSE)
             return template.render(eni=eni)
diff --git a/moto/ec2/responses/subnets.py b/moto/ec2/responses/subnets.py
--- a/moto/ec2/responses/subnets.py
+++ b/moto/ec2/responses/subnets.py
@@ -53,7 +53,7 @@ def modify_subnet_attribute(self):
     <state>pending</state>
     <vpcId>{{ subnet.vpc_id }}</vpcId>
     <cidrBlock>{{ subnet.cidr_block }}</cidrBlock>
-    <availableIpAddressCount>251</availableIpAddressCount>
+    <availableIpAddressCount>{{ subnet.available_ip_addresses }}</availableIpAddressCount>
     <availabilityZone>{{ subnet._availability_zone.name }}</availabilityZone>
     <availabilityZoneId>{{ subnet._availability_zone.zone_id }}</availabilityZoneId>
     <defaultForAz>{{ subnet.default_for_az }}</defaultForAz>
@@ -81,7 +81,7 @@ def modify_subnet_attribute(self):
         <state>available</state>
         <vpcId>{{ subnet.vpc_id }}</vpcId>
         <cidrBlock>{{ subnet.cidr_block }}</cidrBlock>
-        <availableIpAddressCount>251</availableIpAddressCount>
+        <availableIpAddressCount>{{ subnet.available_ip_addresses }}</availableIpAddressCount>
         <availabilityZone>{{ subnet._availability_zone.name }}</availabilityZone>
         <availabilityZoneId>{{ subnet._availability_zone.zone_id }}</availabilityZoneId>
         <defaultForAz>{{ subnet.default_for_az }}</defaultForAz>


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
@@ -11,6 +11,7 @@
 from botocore.exceptions import ParamValidationError, ClientError
 import json
 import sure  # noqa
+import random
 
 from moto import mock_cloudformation_deprecated, mock_ec2, mock_ec2_deprecated
 
@@ -474,3 +475,127 @@ def test_create_subnets_with_overlapping_cidr_blocks():
             subnet_cidr_block
         )
     )
+
+
+@mock_ec2
+def test_available_ip_addresses_in_subnet():
+    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")
+    cidr_range_addresses = [
+        ("10.0.0.0/16", 65531),
+        ("10.0.0.0/17", 32763),
+        ("10.0.0.0/18", 16379),
+        ("10.0.0.0/19", 8187),
+        ("10.0.0.0/20", 4091),
+        ("10.0.0.0/21", 2043),
+        ("10.0.0.0/22", 1019),
+        ("10.0.0.0/23", 507),
+        ("10.0.0.0/24", 251),
+        ("10.0.0.0/25", 123),
+        ("10.0.0.0/26", 59),
+        ("10.0.0.0/27", 27),
+        ("10.0.0.0/28", 11),
+    ]
+    for (cidr, expected_count) in cidr_range_addresses:
+        validate_subnet_details(client, vpc, cidr, expected_count)
+
+
+@mock_ec2
+def test_available_ip_addresses_in_subnet_with_enis():
+    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")
+    # Verify behaviour for various CIDR ranges (...)
+    # Don't try to assign ENIs to /27 and /28, as there are not a lot of IP addresses to go around
+    cidr_range_addresses = [
+        ("10.0.0.0/16", 65531),
+        ("10.0.0.0/17", 32763),
+        ("10.0.0.0/18", 16379),
+        ("10.0.0.0/19", 8187),
+        ("10.0.0.0/20", 4091),
+        ("10.0.0.0/21", 2043),
+        ("10.0.0.0/22", 1019),
+        ("10.0.0.0/23", 507),
+        ("10.0.0.0/24", 251),
+        ("10.0.0.0/25", 123),
+        ("10.0.0.0/26", 59),
+    ]
+    for (cidr, expected_count) in cidr_range_addresses:
+        validate_subnet_details_after_creating_eni(client, vpc, cidr, expected_count)
+
+
+def validate_subnet_details(client, vpc, cidr, expected_ip_address_count):
+    subnet = client.create_subnet(
+        VpcId=vpc.id, CidrBlock=cidr, AvailabilityZone="us-west-1b"
+    )["Subnet"]
+    subnet["AvailableIpAddressCount"].should.equal(expected_ip_address_count)
+    client.delete_subnet(SubnetId=subnet["SubnetId"])
+
+
+def validate_subnet_details_after_creating_eni(
+    client, vpc, cidr, expected_ip_address_count
+):
+    subnet = client.create_subnet(
+        VpcId=vpc.id, CidrBlock=cidr, AvailabilityZone="us-west-1b"
+    )["Subnet"]
+    # Create a random number of Elastic Network Interfaces
+    nr_of_eni_to_create = random.randint(0, 5)
+    ip_addresses_assigned = 0
+    enis_created = []
+    for i in range(0, nr_of_eni_to_create):
+        # Create a random number of IP addresses per ENI
+        nr_of_ip_addresses = random.randint(1, 5)
+        if nr_of_ip_addresses == 1:
+            # Pick the first available IP address (First 4 are reserved by AWS)
+            private_address = "10.0.0." + str(ip_addresses_assigned + 4)
+            eni = client.create_network_interface(
+                SubnetId=subnet["SubnetId"], PrivateIpAddress=private_address
+            )["NetworkInterface"]
+            enis_created.append(eni)
+            ip_addresses_assigned = ip_addresses_assigned + 1
+        else:
+            # Assign a list of IP addresses
+            private_addresses = [
+                "10.0.0." + str(4 + ip_addresses_assigned + i)
+                for i in range(0, nr_of_ip_addresses)
+            ]
+            eni = client.create_network_interface(
+                SubnetId=subnet["SubnetId"],
+                PrivateIpAddresses=[
+                    {"PrivateIpAddress": address} for address in private_addresses
+                ],
+            )["NetworkInterface"]
+            enis_created.append(eni)
+            ip_addresses_assigned = ip_addresses_assigned + nr_of_ip_addresses + 1  #
+    # Verify that the nr of available IP addresses takes these ENIs into account
+    updated_subnet = client.describe_subnets(SubnetIds=[subnet["SubnetId"]])["Subnets"][
+        0
+    ]
+    private_addresses = [
+        eni["PrivateIpAddress"] for eni in enis_created if eni["PrivateIpAddress"]
+    ]
+    for eni in enis_created:
+        private_addresses.extend(
+            [address["PrivateIpAddress"] for address in eni["PrivateIpAddresses"]]
+        )
+    error_msg = (
+        "Nr of IP addresses for Subnet with CIDR {0} is incorrect. Expected: {1}, Actual: {2}. "
+        "Addresses: {3}"
+    )
+    with sure.ensure(
+        error_msg,
+        cidr,
+        str(expected_ip_address_count),
+        updated_subnet["AvailableIpAddressCount"],
+        str(private_addresses),
+    ):
+        updated_subnet["AvailableIpAddressCount"].should.equal(
+            expected_ip_address_count - ip_addresses_assigned
+        )
+    # Clean up, as we have to create a few more subnets that shouldn't interfere with each other
+    for eni in enis_created:
+        client.delete_network_interface(NetworkInterfaceId=eni["NetworkInterfaceId"])
+    client.delete_subnet(SubnetId=subnet["SubnetId"])