diff --git a/IMPLEMENTATION_COVERAGE.md b/IMPLEMENTATION_COVERAGE.md
index 5d9f18ebf..6a3b80cbd 100644
--- a/IMPLEMENTATION_COVERAGE.md
+++ b/IMPLEMENTATION_COVERAGE.md
@@ -3447,7 +3447,7 @@
- [X] list_signing_certificates
- [ ] list_ssh_public_keys
- [X] list_user_policies
-- [ ] list_user_tags
+- [X] list_user_tags
- [X] list_users
- [X] list_virtual_mfa_devices
- [X] put_group_policy
diff --git a/moto/iam/models.py b/moto/iam/models.py
index 5bbd9235d..18b3a7a6f 100644
--- a/moto/iam/models.py
+++ b/moto/iam/models.py
@@ -543,7 +543,7 @@ class Group(BaseModel):
class User(BaseModel):
- def __init__(self, name, path=None):
+ def __init__(self, name, path=None, tags=None):
self.name = name
self.id = random_resource_id()
self.path = path if path else "/"
@@ -556,6 +556,7 @@ class User(BaseModel):
self.password = None
self.password_reset_required = False
self.signing_certificates = {}
+ self.tags = tags
@property
def arn(self):
@@ -1421,13 +1422,13 @@ class IAMBackend(BaseBackend):
"The group with name {0} cannot be found.".format(group_name)
)
- def create_user(self, user_name, path="/"):
+ def create_user(self, user_name, path="/", tags=None):
if user_name in self.users:
raise IAMConflictException(
"EntityAlreadyExists", "User {0} already exists".format(user_name)
)
- user = User(user_name, path)
+ user = User(user_name, path, tags)
self.users[user_name] = user
return user
@@ -1583,6 +1584,10 @@ class IAMBackend(BaseBackend):
user = self.get_user(user_name)
return user.policies.keys()
+ def list_user_tags(self, user_name):
+ user = self.get_user(user_name)
+ return user.tags
+
def put_user_policy(self, user_name, policy_name, policy_json):
user = self.get_user(user_name)
diff --git a/moto/iam/responses.py b/moto/iam/responses.py
index 45bd28c36..06561d4c4 100644
--- a/moto/iam/responses.py
+++ b/moto/iam/responses.py
@@ -440,8 +440,8 @@ class IamResponse(BaseResponse):
def create_user(self):
user_name = self._get_param("UserName")
path = self._get_param("Path")
-
- user = iam_backend.create_user(user_name, path)
+ tags = self._get_multi_param("Tags.member")
+ user = iam_backend.create_user(user_name, path, tags)
template = self.response_template(USER_TEMPLATE)
return template.render(action="Create", user=user)
@@ -538,6 +538,12 @@ class IamResponse(BaseResponse):
template = self.response_template(LIST_USER_POLICIES_TEMPLATE)
return template.render(policies=policies)
+ def list_user_tags(self):
+ user_name = self._get_param("UserName")
+ tags = iam_backend.list_user_tags(user_name)
+ template = self.response_template(LIST_USER_TAGS_TEMPLATE)
+ return template.render(user_tags=tags or [])
+
def put_user_policy(self):
user_name = self._get_param("UserName")
policy_name = self._get_param("PolicyName")
@@ -1699,6 +1705,23 @@ LIST_USER_POLICIES_TEMPLATE = """
"""
+LIST_USER_TAGS_TEMPLATE = """
+
+
+ {% for tag in user_tags %}
+ -
+ {{ tag.Key }}
+ {{ tag.Value }}
+
+ {% endfor %}
+
+ false
+
+
+ 7a62c49f-347e-4fc4-9331-6e8eEXAMPLE
+
+"""
+
CREATE_ACCESS_KEY_TEMPLATE = """
diff --git a/tests/test_iam/test_iam.py b/tests/test_iam/test_iam.py
index 6311dce9c..cabb6d037 100644
--- a/tests/test_iam/test_iam.py
+++ b/tests/test_iam/test_iam.py
@@ -1737,9 +1737,7 @@ def test_delete_saml_provider():
def test_create_role_defaults():
"""Tests default values"""
conn = boto3.client("iam", region_name="us-east-1")
- conn.create_role(
- RoleName="my-role", AssumeRolePolicyDocument="{}",
- )
+ conn.create_role(RoleName="my-role", AssumeRolePolicyDocument="{}")
# Get role:
role = conn.get_role(RoleName="my-role")["Role"]
@@ -2672,3 +2670,56 @@ def test_get_account_summary():
"GroupsQuota": 300,
}
)
+
+
+@mock_iam()
+def test_list_user_tags():
+ """Tests both setting a tags on a user in create_user and list_user_tags"""
+ conn = boto3.client("iam", region_name="us-east-1")
+ conn.create_user(UserName="kenny-bania")
+ conn.create_user(
+ UserName="jackie-chiles", Tags=[{"Key": "Sue-Allen", "Value": "Oh-Henry"}]
+ )
+ conn.create_user(
+ UserName="cosmo",
+ Tags=[
+ {"Key": "Stan", "Value": "The Caddy"},
+ {"Key": "like-a", "Value": "glove"},
+ ],
+ )
+
+ assert conn.list_user_tags(UserName="kenny-bania") == {
+ "Tags": [],
+ "IsTruncated": False,
+ "ResponseMetadata": {
+ "RequestId": "7a62c49f-347e-4fc4-9331-6e8eEXAMPLE",
+ "HTTPStatusCode": 200,
+ "HTTPHeaders": {"server": "amazon.com"},
+ "RetryAttempts": 0,
+ },
+ }
+
+ assert conn.list_user_tags(UserName="jackie-chiles") == {
+ "Tags": [{"Key": "Sue-Allen", "Value": "Oh-Henry"}],
+ "IsTruncated": False,
+ "ResponseMetadata": {
+ "RequestId": "7a62c49f-347e-4fc4-9331-6e8eEXAMPLE",
+ "HTTPStatusCode": 200,
+ "HTTPHeaders": {"server": "amazon.com"},
+ "RetryAttempts": 0,
+ },
+ }
+
+ assert conn.list_user_tags(UserName="cosmo") == {
+ "Tags": [
+ {"Key": "Stan", "Value": "The Caddy"},
+ {"Key": "like-a", "Value": "glove"},
+ ],
+ "IsTruncated": False,
+ "ResponseMetadata": {
+ "RequestId": "7a62c49f-347e-4fc4-9331-6e8eEXAMPLE",
+ "HTTPStatusCode": 200,
+ "HTTPHeaders": {"server": "amazon.com"},
+ "RetryAttempts": 0,
+ },
+ }