Repository: getmoto/moto

Issue:
Fix busted availableIpAddressCount in EC2 subnets
Identified using LocalStack 0.11.0 which uses Moto 1.3.15something? (started via the Docker image `localstack/localstack:0.11.0`)

The attribute `availableIpAddressCount` in `DescribeSubnets` output is empty?

Ran `aws ec2 describe-subnets` against EC2 vs LocalStack, spotted the difference. Causes `awscli` to exit with this on any Subnet API calls (includes create, describe):

```
ValueError: invalid literal for int() with base 10: ''
```

------------------------------

EC2 response:

```
<DescribeSubnetsResponse xmlns="http://ec2.amazonaws.com/doc/2016-11-15/">
    <requestId>REDACTED-UUID</requestId>
    <subnetSet>
        <item>
            <subnetId>subnet-REDACTED</subnetId>
            <subnetArn>arn:aws:ec2:us-east-1:REDACTED:subnet/subnet-REDACTED</subnetArn>
            <state>available</state>
            <ownerId>REDACTED</ownerId>
            <vpcId>vpc-REDACTED</vpcId>
            <cidrBlock>10.REDACTED/24</cidrBlock>
            <ipv6CidrBlockAssociationSet/>
            <availableIpAddressCount>190</availableIpAddressCount>
            <availabilityZone>us-east-1a</availabilityZone>
            <availabilityZoneId>use1-az4</availabilityZoneId>
            <defaultForAz>false</defaultForAz>
            <mapPublicIpOnLaunch>true</mapPublicIpOnLaunch>
            <tagSet>
                <item>
                    <key>Name</key>
                    <value>REDACTED</value>
                </item>
                <item>
                    <key>#</key>
                    <value>1</value>
                </item>
            </tagSet>
            <assignIpv6AddressOnCreation>false</assignIpv6AddressOnCreation>
        </item>
...
```

LocalStack (Moto) response:

```
<DescribeSubnetsResponse xmlns="http://ec2.amazonaws.com/doc/2013-10-15/">
  <requestId>7a62c49f-347e-4fc4-9331-6e8eEXAMPLE</requestId>
  <subnetSet>
    <item>
      <subnetId>subnet-9eaffe1d</subnetId>
      <state>available</state>
      <vpcId>vpc-08c7fa4d</vpcId>
      <cidrBlock>172.31.0.0/20</cidrBlock>
      <availableIpAddressCount></availableIpAddressCount>
      <availabilityZone>us-east-1a</availabilityZone>
      <availabilityZoneId>use1-az6</availabilityZoneId>
      <defaultForAz>true</defaultForAz>
      <mapPublicIpOnLaunch>true</mapPublicIpOnLaunch>
      <ownerId>123456789012</ownerId>
      <assignIpv6AddressOnCreation>False</assignIpv6AddressOnCreation>
      <ipv6CidrBlockAssociationSet>[]</ipv6CidrBlockAssociationSet>
      <subnetArn>arn:aws:ec2:us-east-1:123456789012:subnet/subnet-9eaffe1d</subnetArn>
    </item>
...
```


Discussion:

Resolving patch:
diff --git a/moto/ec2/models.py b/moto/ec2/models.py
--- a/moto/ec2/models.py
+++ b/moto/ec2/models.py
@@ -775,7 +775,14 @@ def prep_nics(self, nic_spec, private_ip=None, associate_public_ip=None):
                 if "SubnetId" in nic:
                     subnet = self.ec2_backend.get_subnet(nic["SubnetId"])
                 else:
-                    subnet = None
+                    # Get default Subnet
+                    subnet = [
+                        subnet
+                        for subnet in self.ec2_backend.get_all_subnets(
+                            filters={"availabilityZone": self._placement.zone}
+                        )
+                        if subnet.default_for_az
+                    ][0]
 
                 group_id = nic.get("SecurityGroupId")
                 group_ids = [group_id] if group_id else []


Test patch:
diff --git a/tests/test_cloudformation/test_cloudformation_stack_integration.py b/tests/test_cloudformation/test_cloudformation_stack_integration.py
--- a/tests/test_cloudformation/test_cloudformation_stack_integration.py
+++ b/tests/test_cloudformation/test_cloudformation_stack_integration.py
@@ -495,7 +495,7 @@ def test_autoscaling_group_with_elb():
             "my-as-group": {
                 "Type": "AWS::AutoScaling::AutoScalingGroup",
                 "Properties": {
-                    "AvailabilityZones": ["us-east1"],
+                    "AvailabilityZones": ["us-east-1a"],
                     "LaunchConfigurationName": {"Ref": "my-launch-config"},
                     "MinSize": "2",
                     "MaxSize": "2",
@@ -522,7 +522,7 @@ def test_autoscaling_group_with_elb():
             "my-elb": {
                 "Type": "AWS::ElasticLoadBalancing::LoadBalancer",
                 "Properties": {
-                    "AvailabilityZones": ["us-east1"],
+                    "AvailabilityZones": ["us-east-1a"],
                     "Listeners": [
                         {
                             "LoadBalancerPort": "80",
@@ -545,10 +545,10 @@ def test_autoscaling_group_with_elb():
 
     web_setup_template_json = json.dumps(web_setup_template)
 
-    conn = boto.cloudformation.connect_to_region("us-west-1")
+    conn = boto.cloudformation.connect_to_region("us-east-1")
     conn.create_stack("web_stack", template_body=web_setup_template_json)
 
-    autoscale_conn = boto.ec2.autoscale.connect_to_region("us-west-1")
+    autoscale_conn = boto.ec2.autoscale.connect_to_region("us-east-1")
     autoscale_group = autoscale_conn.get_all_groups()[0]
     autoscale_group.launch_config_name.should.contain("my-launch-config")
     autoscale_group.load_balancers[0].should.equal("my-elb")
@@ -557,7 +557,7 @@ def test_autoscaling_group_with_elb():
     autoscale_conn.get_all_launch_configurations().should.have.length_of(1)
 
     # Confirm the ELB was actually created
-    elb_conn = boto.ec2.elb.connect_to_region("us-west-1")
+    elb_conn = boto.ec2.elb.connect_to_region("us-east-1")
     elb_conn.get_all_load_balancers().should.have.length_of(1)
 
     stack = conn.describe_stacks()[0]
@@ -584,7 +584,7 @@ def test_autoscaling_group_with_elb():
     elb_resource.physical_resource_id.should.contain("my-elb")
 
     # confirm the instances were created with the right tags
-    ec2_conn = boto.ec2.connect_to_region("us-west-1")
+    ec2_conn = boto.ec2.connect_to_region("us-east-1")
     reservations = ec2_conn.get_all_reservations()
     len(reservations).should.equal(1)
     reservation = reservations[0]
@@ -604,7 +604,7 @@ def test_autoscaling_group_update():
             "my-as-group": {
                 "Type": "AWS::AutoScaling::AutoScalingGroup",
                 "Properties": {
-                    "AvailabilityZones": ["us-west-1"],
+                    "AvailabilityZones": ["us-west-1a"],
                     "LaunchConfigurationName": {"Ref": "my-launch-config"},
                     "MinSize": "2",
                     "MaxSize": "2",
diff --git a/tests/test_ec2/test_instances.py b/tests/test_ec2/test_instances.py
--- a/tests/test_ec2/test_instances.py
+++ b/tests/test_ec2/test_instances.py
@@ -71,7 +71,7 @@ def test_instance_launch_and_terminate():
     instance.id.should.equal(instance.id)
     instance.state.should.equal("running")
     instance.launch_time.should.equal("2014-01-01T05:00:00.000Z")
-    instance.vpc_id.should.equal(None)
+    instance.vpc_id.shouldnt.equal(None)
     instance.placement.should.equal("us-east-1a")
 
     root_device_name = instance.root_device_name
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
@@ -599,3 +599,20 @@ def validate_subnet_details_after_creating_eni(
     for eni in enis_created:
         client.delete_network_interface(NetworkInterfaceId=eni["NetworkInterfaceId"])
     client.delete_subnet(SubnetId=subnet["SubnetId"])
+
+
+@mock_ec2
+def test_run_instances_should_attach_to_default_subnet():
+    # https://github.com/spulec/moto/issues/2877
+    ec2 = boto3.resource("ec2", region_name="us-west-1")
+    client = boto3.client("ec2", region_name="us-west-1")
+    ec2.create_security_group(GroupName="sg01", Description="Test security group sg01")
+    # run_instances
+    instances = client.run_instances(MinCount=1, MaxCount=1, SecurityGroups=["sg01"],)
+    # Assert subnet is created appropriately
+    subnets = client.describe_subnets()["Subnets"]
+    default_subnet_id = subnets[0]["SubnetId"]
+    instances["Instances"][0]["NetworkInterfaces"][0]["SubnetId"].should.equal(
+        default_subnet_id
+    )
+    subnets[0]["AvailableIpAddressCount"].should.equal(4090)