Repository: getmoto/moto (python)

Issue:
Implement EC2 DescribeImages filter 'source-instance-id'

Documentation: https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeImages.html

If I define the filter 'source-instance-id', it returns the error: The filter 'source-instance-id' for DescribeImages has not been implemented in Moto yet. Feel free to open an issue at https://github.com/getmoto/moto/issues

Pull request description:
EC2: implement `source-instance-id` filter for DescribeImages
Closes #9047 

Patch:
diff --git a/moto/ec2/models/amis.py b/moto/ec2/models/amis.py
index 266584e30..c7b1ed110 100644
--- a/moto/ec2/models/amis.py
+++ b/moto/ec2/models/amis.py
@@ -78,6 +78,7 @@ class Ami(TaggedEC2Resource):
         self.creation_date = creation_date or utc_date_and_time()
         self.product_codes = product_codes
         self.boot_mode = boot_mode
+        self.instance_id: Optional[str] = None
 
         if tags is not None:
             self.add_tags(tags)
@@ -128,6 +129,10 @@ class Ami(TaggedEC2Resource):
     def is_public_string(self) -> str:
         return str(self.is_public).lower()
 
+    @property
+    def source_instance_id(self) -> Optional[str]:
+        return self.instance_id
+
     def get_filter_value(
         self, filter_name: str, method_name: Optional[str] = None
     ) -> Any:
@@ -153,6 +158,8 @@ class Ami(TaggedEC2Resource):
             return self.product_codes
         elif filter_name == "product-code.type":
             return "marketplace"  # devpay is not (yet?) supported
+        elif filter_name == "source-instance-id":
+            return self.source_instance_id
         else:
             return super().get_filter_value(filter_name, "DescribeImages")
 
diff --git a/moto/ec2/responses/amis.py b/moto/ec2/responses/amis.py
index 9b9d06877..61f68fe49 100644
--- a/moto/ec2/responses/amis.py
+++ b/moto/ec2/responses/amis.py
@@ -215,6 +215,9 @@ DESCRIBE_IMAGES_RESPONSE = """<DescribeImagesResponse xmlns="http://ec2.amazonaw
             {% endfor %}
           </tagSet>
           <hypervisor>xen</hypervisor>
+          {% if image.source_instance_id %}
+          <sourceInstanceId>{{ image.source_instance_id }}</sourceInstanceId>
+          {% endif %}
         </item>
     {% endfor %}
   </imagesSet>


Test patch:
diff --git a/tests/test_ec2/test_amis.py b/tests/test_ec2/test_amis.py
index f8b78f5c0..541d8aff2 100644
--- a/tests/test_ec2/test_amis.py
+++ b/tests/test_ec2/test_amis.py
@@ -1311,6 +1311,36 @@ def test_ami_filter_by_empty_tag():
     assert len(client.describe_images(Filters=images_filter)["Images"]) == 3
 
 
+@mock_aws
+def test_ami_filter_by_source_instance_id():
+    ec2 = boto3.resource("ec2", region_name="us-west-1")
+    client = boto3.client("ec2", region_name="us-west-1")
+
+    instance_ids = []
+    for i in range(2):
+        instance = ec2.create_instances(ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1)[
+            0
+        ]
+        instance_ids.append(instance.instance_id)
+    for i in range(2):
+        client.create_image(
+            InstanceId=instance_ids[i],
+            Name=f"MyAMI{i}",
+            Description="Image from instance {i}",
+        )
+    for i in range(2):
+        images_filter = [
+            {
+                "Name": "source-instance-id",
+                "Values": [instance_ids[i]],
+            },
+        ]
+        resp = client.describe_images(Filters=images_filter)
+        images = resp["Images"]
+        assert len(images) == 1
+        assert images[0]["SourceInstanceId"] == instance_ids[i]
+
+
 @mock.patch.dict(os.environ, {"MOTO_EC2_LOAD_DEFAULT_AMIS": "true"})
 @mock_aws
 def test_ami_filter_by_ownerid():