Repository: getmoto/moto

Issue:
create_image() TagSpecification is not implemented
Hi, I think that the TagSpecification on create_images() is not working/not implemented

````
  instances = ec2.run_instances(ImageId='foo', MinCount=1, MaxCount=1)["Instances"]
  image = ec2.create_image(InstanceId=instances[0]["InstanceId"],
                                     Name=f"MyFooAMI{i}",
                                     Description="Test",
                                     TagSpecifications=[
                                         {
                                             'ResourceType': 'image',
                                             'Tags': [
                                                 {
                                                     'Key': 'Base_AMI_Name',
                                                     'Value': 'Deep Learning Base AMI (Amazon Linux 2) Version 31.0',
                                                 },
                                                 {
                                                     'Key': 'OS_Version',
                                                     'Value': 'AWS Linux 2',
                                                 },
                                             ]
                                         },
                                     ]
                                     )
````

Following by:

```
ec2.describe_images(Owners=["self"])["Images"][0]['Tags']
```
**Result:**
This shows that the image tags are empty.

**WORKAROUND**:
I've tried adding the tags later with:
`````
response = ec2.create_tags(Resources=[image["ImageId"]],
                                       Tags=[
                                           {
                                               'Key': 'Base_AMI_Name',
                                               'Value': 'Deep Learning Base AMI (Amazon Linux 2) Version 31.0',
                                           },
                                           {
                                               'Key': 'OS_Version',
                                               'Value': 'AWS Linux 2',
                                           },
                                       ])
````

And when using the create_tags method the describe_images method shows the tags. 
Since there is this workaround is not a problem but it may be good to have it noted somewhere as Ive seen this has happened also in other services (EBS volumes...) on the issues.

It would be great if either a warning could be raised or implemented.

Thanks for the library!




Discussion:

Resolving patch:
diff --git a/moto/ec2/exceptions.py b/moto/ec2/exceptions.py
--- a/moto/ec2/exceptions.py
+++ b/moto/ec2/exceptions.py
@@ -615,3 +615,13 @@ def __init__(self, vpc_end_point_id):
             "InvalidVpcEndPointId.NotFound",
             "The VpcEndPoint ID '{0}' does not exist".format(vpc_end_point_id),
         )
+
+
+class InvalidTaggableResourceType(EC2ClientError):
+    def __init__(self, resource_type):
+        super(InvalidTaggableResourceType, self).__init__(
+            "InvalidParameterValue",
+            "'{}' is not a valid taggable resource type for this operation.".format(
+                resource_type
+            ),
+        )
diff --git a/moto/ec2/models.py b/moto/ec2/models.py
--- a/moto/ec2/models.py
+++ b/moto/ec2/models.py
@@ -109,6 +109,7 @@
     IncorrectStateIamProfileAssociationError,
     InvalidAssociationIDIamProfileAssociationError,
     InvalidVpcEndPointIdError,
+    InvalidTaggableResourceType,
 )
 from .utils import (
     EC2_RESOURCE_TO_PREFIX,
@@ -1522,10 +1523,26 @@ def _load_amis(self):
             ami_id = ami["ami_id"]
             self.amis[ami_id] = Ami(self, **ami)
 
-    def create_image(self, instance_id, name=None, description=None, context=None):
+    def create_image(
+        self,
+        instance_id,
+        name=None,
+        description=None,
+        context=None,
+        tag_specifications=None,
+    ):
         # TODO: check that instance exists and pull info from it.
         ami_id = random_ami_id()
         instance = self.get_instance(instance_id)
+        tags = []
+        for tag_specification in tag_specifications:
+            resource_type = tag_specification["ResourceType"]
+            if resource_type == "image":
+                tags += tag_specification["Tag"]
+            elif resource_type == "snapshot":
+                raise NotImplementedError()
+            else:
+                raise InvalidTaggableResourceType(resource_type)
 
         ami = Ami(
             self,
@@ -1536,6 +1553,8 @@ def create_image(self, instance_id, name=None, description=None, context=None):
             description=description,
             owner_id=OWNER_ID,
         )
+        for tag in tags:
+            ami.add_tag(tag["Key"], tag["Value"])
         self.amis[ami_id] = ami
         return ami
 
diff --git a/moto/ec2/responses/amis.py b/moto/ec2/responses/amis.py
--- a/moto/ec2/responses/amis.py
+++ b/moto/ec2/responses/amis.py
@@ -8,9 +8,14 @@ def create_image(self):
         name = self.querystring.get("Name")[0]
         description = self._get_param("Description", if_none="")
         instance_id = self._get_param("InstanceId")
+        tag_specifications = self._get_multi_param("TagSpecification")
         if self.is_not_dryrun("CreateImage"):
             image = self.ec2_backend.create_image(
-                instance_id, name, description, context=self
+                instance_id,
+                name,
+                description,
+                context=self,
+                tag_specifications=tag_specifications,
             )
             template = self.response_template(CREATE_IMAGE_RESPONSE)
             return template.render(image=image)


Test patch:
diff --git a/tests/test_ec2/test_amis.py b/tests/test_ec2/test_amis.py
--- a/tests/test_ec2/test_amis.py
+++ b/tests/test_ec2/test_amis.py
@@ -876,3 +876,48 @@ def test_ami_snapshots_have_correct_owner():
 
         for snapshot in snapshots_rseponse["Snapshots"]:
             assert owner_id == snapshot["OwnerId"]
+
+
+@mock_ec2
+def test_create_image_with_tag_specification():
+    ec2 = boto3.resource("ec2", region_name="us-west-1")
+    client = boto3.client("ec2", region_name="us-west-1")
+    tag_specifications = [
+        {
+            "ResourceType": "image",
+            "Tags": [
+                {
+                    "Key": "Base_AMI_Name",
+                    "Value": "Deep Learning Base AMI (Amazon Linux 2) Version 31.0",
+                },
+                {"Key": "OS_Version", "Value": "AWS Linux 2",},
+            ],
+        },
+    ]
+    instance = ec2.create_instances(ImageId=EXAMPLE_AMI_ID, MinCount=1, MaxCount=1)[0]
+    image_id = client.create_image(
+        InstanceId=instance.instance_id,
+        Name="test-image",
+        Description="test ami",
+        TagSpecifications=tag_specifications,
+    )["ImageId"]
+
+    image = client.describe_images(ImageIds=[image_id])["Images"][0]
+    image["Tags"].should.equal(tag_specifications[0]["Tags"])
+
+    with pytest.raises(ClientError) as ex:
+        client.create_image(
+            InstanceId=instance.instance_id,
+            Name="test-image",
+            Description="test ami",
+            TagSpecifications=[
+                {
+                    "ResourceType": "invalid-resource-type",
+                    "Tags": [{"Key": "key", "Value": "value"}],
+                }
+            ],
+        )
+    ex.value.response["Error"]["Code"].should.equal("InvalidParameterValue")
+    ex.value.response["Error"]["Message"].should.equal(
+        "'invalid-resource-type' is not a valid taggable resource type for this operation."
+    )