Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion moto/ec2/models/instances.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,13 @@ def __init__(
tags = convert_tag_spec(tag_spec_set)
instance_tags = tags.get("instance", {})
self.add_tags(instance_tags)
# Add AWS-managed launch template tags
self.add_tags(
{
"aws:ec2launchtemplate:id": template_version.template.id,
"aws:ec2launchtemplate:version": str(template_version.number),
}
)

self._state = InstanceState("running", 16)
self._reason = ""
Expand Down Expand Up @@ -1098,6 +1105,6 @@ def _get_template_from_args(
template_names=[launch_template_arg["LaunchTemplateName"]]
)[0]
)
version = launch_template_arg.get("Version", template.latest_version_number)
version = launch_template_arg.get("Version", template.default_version_number)
template_version = template.get_version(version)
return template_version
121 changes: 107 additions & 14 deletions tests/test_ec2/test_instances.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from moto import mock_aws, settings
from moto.core import DEFAULT_ACCOUNT_ID as ACCOUNT_ID
from moto.core.types import Base64EncodedString
from tests import EXAMPLE_AMI_ID
from tests import EXAMPLE_AMI_ID, allow_aws_request
from tests.test_ec2 import ec2_aws_verified

from .helpers import assert_dryrun_error
Expand Down Expand Up @@ -2491,27 +2491,120 @@ def test_create_instance_with_launch_template_id_produces_no_warning(
assert all("Could not find AMI" not in msg for msg in messages)


@mock_aws
def test_create_instance_from_launch_template__process_tags():
client = boto3.client("ec2", region_name="us-west-1")
@ec2_aws_verified()
@pytest.mark.aws_verified
@pytest.mark.parametrize(
["create_version_2", "change_default", "explicit_version", "expected_version"],
[
pytest.param(False, False, None, "1", id="default-v1-only"),
pytest.param(True, False, None, "1", id="default-v1-with-v2-exists"),
pytest.param(True, True, None, "2", id="default-changed-to-v2"),
pytest.param(True, False, "1", "1", id="explicit-v1"),
pytest.param(True, False, "2", "2", id="explicit-v2"),
pytest.param(False, False, "$Latest", "1", id="dollar-latest-v1-only"),
pytest.param(True, False, "$Latest", "2", id="dollar-latest-with-v2"),
pytest.param(False, False, "$Default", "1", id="dollar-default-v1"),
pytest.param(
True, False, "$Default", "1", id="dollar-default-v1-with-v2-exists"
),
pytest.param(True, True, "$Default", "2", id="dollar-default-v2-after-change"),
],
)
def test_create_instance_from_launch_template__aws_managed_tags(
create_version_2,
change_default,
explicit_version,
expected_version,
ec2_client=None,
):
"""Test that AWS-managed launch template tags are added and version resolution works correctly.

This test verifies:
1. AWS-managed tags (aws:ec2launchtemplate:id and aws:ec2launchtemplate:version) are added
2. Default version (not latest) is used when no version is specified
3. Numeric versions (1, 2) work correctly
4. Special version identifiers ($Latest, $Default) work correctly
"""
if allow_aws_request():
# Running against real AWS - get latest Amazon Linux AMI
ssm_client = boto3.client("ssm", region_name=ec2_client.meta.region_name)
kernel_61 = "/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-6.1-x86_64"
ami_id = ssm_client.get_parameter(Name=kernel_61)["Parameter"]["Value"]
else:
# Running in mock mode - use test AMI
ami_id = EXAMPLE_AMI_ID

template = client.create_launch_template(
LaunchTemplateName=str(uuid4()),
template_name = str(uuid4())
template = ec2_client.create_launch_template(
LaunchTemplateName=template_name,
LaunchTemplateData={
"ImageId": EXAMPLE_AMI_ID,
"ImageId": ami_id,
"TagSpecifications": [
{"ResourceType": "instance", "Tags": [{"Key": "k", "Value": "v"}]}
{"ResourceType": "instance", "Tags": [{"Key": "k", "Value": "v1"}]}
],
},
)["LaunchTemplate"]
template_id = template["LaunchTemplateId"]

try:
# Optionally create version 2
if create_version_2:
ec2_client.create_launch_template_version(
LaunchTemplateId=template_id,
LaunchTemplateData={
"ImageId": ami_id,
"TagSpecifications": [
{
"ResourceType": "instance",
"Tags": [{"Key": "k", "Value": "v2"}],
}
],
},
)

instance = client.run_instances(
MinCount=1,
MaxCount=1,
LaunchTemplate={"LaunchTemplateId": template["LaunchTemplateId"]},
)["Instances"][0]
# Optionally change default version to 2
if change_default:
ec2_client.modify_launch_template(
LaunchTemplateId=template_id,
DefaultVersion="2",
)

# Build launch template specification
launch_template_spec = {"LaunchTemplateId": template_id}
if explicit_version:
launch_template_spec["Version"] = explicit_version

# Run instance
instance = ec2_client.run_instances(
MinCount=1,
MaxCount=1,
LaunchTemplate=launch_template_spec,
)["Instances"][0]
instance_id = instance["InstanceId"]

try:
# Verify AWS-managed tags are present
tags = {tag["Key"]: tag["Value"] for tag in instance["Tags"]}
assert tags["aws:ec2launchtemplate:id"] == template_id, (
f"Expected template ID {template_id}, got {tags.get('aws:ec2launchtemplate:id')}"
)
assert tags["aws:ec2launchtemplate:version"] == expected_version, (
f"Expected version {expected_version}, got {tags.get('aws:ec2launchtemplate:version')}"
)

# Verify user tags from the correct version
expected_tag_value = f"v{expected_version}"
assert tags["k"] == expected_tag_value, (
f"Expected tag value {expected_tag_value}, got {tags.get('k')}"
)

finally:
# Clean up instance
ec2_client.terminate_instances(InstanceIds=[instance_id])

assert instance["Tags"] == [{"Key": "k", "Value": "v"}]
finally:
# Clean up launch template
ec2_client.delete_launch_template(LaunchTemplateId=template_id)


@mock_aws
Expand Down