Repository: getmoto/moto

Issue:
Tags are not added to EC2 volumes on run_instances
The AWS EC2 API will apply tags to volumes when creating a new instance if `TagSpecifications` contains ResourceType volume.

However moto does not have the same behaviour.

```python
from moto import mock_ec2
import boto3

region = "us-west-1"

mock = mock_ec2()
mock.start()

tags = [{"Key": "Name", "Value": "snuffaluffagus"}]
kwargs = {
    "ImageId": "my_ami",
    "MaxCount": 1,
    "MinCount": 1,
    "KeyName": "my_key",
    "InstanceType": "my_instance_type",
    "TagSpecifications": [{"ResourceType": "instance", "Tags": tags}, {"ResourceType": "volume", "Tags": tags}]
}

ec2_client = boto3.client("ec2", region_name=region)

ec2_client.run_instances(**kwargs)
volumes = ec2_client.describe_volumes()

print(volumes)

assert volumes["Volumes"][0]["Tags"][0]["Value"] == "snuffaluffagus"
```

When run with moto 2.0.6:

```
{'Volumes': [{'Attachments': [{'AttachTime': datetime.datetime(2021, 5, 11, 10, 41, 43, tzinfo=tzutc()), 'Device': '/dev/sda1', 'InstanceId': 'i-beb0fc32e456ac884', 'State': 'attached', 'VolumeId': 'vol-a9e674a6', 'DeleteOnTermination': False}], 'AvailabilityZone': '', 'CreateTime': datetime.datetime(2021, 5, 11, 10, 41, 43, tzinfo=tzutc()), 'Encrypted': False, 'Size': 8, 'SnapshotId': '', 'State': 'in-use', 'VolumeId': 'vol-a9e674a6', 'VolumeType': 'standard'}], 'ResponseMetadata': {'RequestId': '59dbff89-35bd-4eac-99ed-be587EXAMPLE', 'HTTPStatusCode': 200, 'HTTPHeaders': {'server': 'amazon.com'}, 'RetryAttempts': 0}}
Traceback (most recent call last):
  File "src/aec/command/test.py", line 26, in <module>
    assert volumes["Volumes"][0]["Tags"][0]["Value"] == "snuffaluffagus"
KeyError: 'Tags'
```


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
@@ -968,6 +968,7 @@ def add_instances(self, image_id, count, user_data, security_group_names, **kwar
 
         tags = kwargs.pop("tags", {})
         instance_tags = tags.get("instance", {})
+        volume_tags = tags.get("volume", {})
 
         for index in range(count):
             kwargs["ami_launch_index"] = index
@@ -996,6 +997,11 @@ def add_instances(self, image_id, count, user_data, security_group_names, **kwar
                     )
             else:
                 new_instance.setup_defaults()
+            # Tag all created volumes.
+            for _, device in new_instance.get_block_device_mapping:
+                volumes = self.describe_volumes(volume_ids=[device.volume_id])
+                for volume in volumes:
+                    volume.add_tags(volume_tags)
 
         return new_reservation
 


Test patch:
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
@@ -338,6 +338,35 @@ def test_create_with_tags():
     len(instances["Instances"][0]["Tags"]).should.equal(3)
 
 
+@mock_ec2
+def test_create_with_volume_tags():
+    ec2 = boto3.client("ec2", region_name="us-west-2")
+    volume_tags = [
+        {"Key": "MY_TAG1", "Value": "MY_VALUE1"},
+        {"Key": "MY_TAG2", "Value": "MY_VALUE2"},
+    ]
+    instances = ec2.run_instances(
+        ImageId=EXAMPLE_AMI_ID,
+        MinCount=2,
+        MaxCount=2,
+        InstanceType="t2.micro",
+        TagSpecifications=[{"ResourceType": "volume", "Tags": volume_tags}],
+    ).get("Instances")
+    instance_ids = [i["InstanceId"] for i in instances]
+    instances = (
+        ec2.describe_instances(InstanceIds=instance_ids)
+        .get("Reservations")[0]
+        .get("Instances")
+    )
+    for instance in instances:
+        instance_volume = instance["BlockDeviceMappings"][0]["Ebs"]
+        volumes = ec2.describe_volumes(VolumeIds=[instance_volume["VolumeId"]]).get(
+            "Volumes"
+        )
+        for volume in volumes:
+            sorted(volume["Tags"], key=lambda i: i["Key"]).should.equal(volume_tags)
+
+
 @mock_ec2_deprecated
 def test_get_instances_filtering_by_state():
     conn = boto.connect_ec2()