The Tags parameter definition is inconsistent across resources.
For example in elasticache.ReplicationGroup we have to define Tag as:
Tags=Tags({'Creator': creator_name}, {'Environment': env_type})
but in ec2.SecurityGroup we have to define as follow:
Tags=[Tag("Creator", creator_name),
Tag("Environment", env_type)])
The early implementation of Tags in troposphere objects was a list of Tags. To reduce the amount of wrapping items with Tag() we switched over to the Tags() object. For backwards compatibility, in a small subset of troposphere, some Tags properties will accept either a list of Tag objects or a Tags object. But all newer troposphere resources only accept the Tags variant.
For your example above, ec2.SecurityGroup does support the Tags variant as well:
$ cat tag.py
from troposphere import Tags
from troposphere.ec2 import SecurityGroup
sg = SecurityGroup(
"sg",
GroupDescription="blank",
Tags=Tags({'Creator': "name"}, {'Environment': "env"}),
)
print(sg.to_dict())
$ python tag.py
{'Properties': {'GroupDescription': 'blank', 'Tags': [{'Key': 'Creator', 'Value': 'name'}, {'Key': 'Environment', 'Value': 'env'}]}, 'Type': 'AWS::EC2::SecurityGroup'}
You should be able to standardize on the Tags() variant across these implementations.
Having said the above, there are other variants of Tags within CloudFormation and, thus, troposphere. There is a json variant which takes a dict and AutoScaling has a special implementation due to the addition of PropagateAtLaunch.
To make matters worse some AWS resources tags are a mapping, not a list, or not called Tags either
To be honest for VPCEndpoint we can add tags using CLI but you cannot add using CF :)