IAM: Omit role tags field from response when not set
This PR fixes a small cosmetic bug which popped up in LocalStack snapshot tests.
Moto IAM CreateRole always returns the Tags field.
$ aws --endpoint-url=http://localhost:5000 iam create-role \
--role-name hello0 \
--assume-role-policy-document '{"Version": "2012-10-17", "Statement": [{"Action": "sts:AssumeRole", "Principal": {"AWS": "123456789012"}, "Effect": "Allow"}]}'
{
"Role": {
"Path": "/",
"RoleName": "hello0",
"RoleId": "AROARZPUZDIKGYSLBPCKW",
"Arn": "arn:aws:iam::123456789012:role/hello0",
"CreateDate": "2025-06-19T12:42:46.393417Z",
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"AWS": "123456789012"
},
"Effect": "Allow"
}
]
},
"MaxSessionDuration": 3600,
"Tags": [],
"RoleLastUsed": {}
}
}
AWS IAM CreateRole only returns the Tags field when specified in the request.
# Without tags
$ aws iam create-role \
--role-name hello1 \
--assume-role-policy-document '{"Version": "2012-10-17", "Statement": [{"Action": "sts:AssumeRole", "Principal": {"AWS": "123456789012"}, "Effect": "Allow"}]}'
{
"Role": {
"Path": "/",
"RoleName": "hello1",
"RoleId": "AROAZCRR4CRXXXXXXXXXX",
"Arn": "arn:aws:iam::123456789012:role/hello1",
"CreateDate": "2025-06-19T12:28:18Z",
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"AWS": "123456789012"
},
"Effect": "Allow"
}
]
}
}
}
# With tags
$ aws iam create-role \
--role-name hello2 \
--assume-role-policy-document '{"Version": "2012-10-17", "Statement": [{"Action": "sts:AssumeRole", "Principal": {"AWS": "123456789012"}, "Effect": "Allow"}]}' --tags Key=colour,Value=red
{
"Role": {
"Path": "/",
"RoleName": "hello2",
"RoleId": "AROAZCRR4CRXXXXXXXXXX",
"Arn": "arn:aws:iam::123456789012:role/hello2",
"CreateDate": "2025-06-19T12:28:42Z",
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"AWS": "123456789012"
},
"Effect": "Allow"
}
]
},
"Tags": [
{
"Key": "colour",
"Value": "red"
}
]
}
}
See https://github.com/getmoto/moto/pull/8970
The tags-attribute is always a list, either empty or not. When serializing, it uses the standard xmltodict.unparse-method to serialize it, which means that for an empty list it will always return <Tags />.
My 'solution' is to explicitly set the tags to None to prevent it from being serialized, but ideally we come up with a better (more generic) solution.
botocoredoes not seem to have any info on whether a field should be serialized when empty, so we cannot use that- In an ideal world this behaviour would be centralized, so empty lists (in all services/responses) should never be serialized. I'd be very surprised if AWS has a standard here though, so we can't do that either.
@bpandola Any ideas on how we could handle this?
Codecov Report
All modified and coverable lines are covered by tests :white_check_mark:
Project coverage is 92.85%. Comparing base (
9cca1cf) to head (ef76579). Report is 1 commits behind head on master.
Additional details and impacted files
@@ Coverage Diff @@
## master #8997 +/- ##
=======================================
Coverage 92.85% 92.85%
=======================================
Files 1287 1287
Lines 112758 112761 +3
=======================================
+ Hits 104700 104703 +3
Misses 8058 8058
| Flag | Coverage Δ | |
|---|---|---|
| servertests | 28.51% <0.00%> (-0.01%) |
:arrow_down: |
| unittests | 92.82% <100.00%> (+<0.01%) |
:arrow_up: |
Flags with carried forward coverage won't be shown. Click here to find out more.
:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.
:rocket: New features to boost your workflow:
- :snowflake: Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
- :package: JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
@bblommers As far as I can tell, the Smithy docs do not explicitly state whether or not empty lists should be serialized (in responses). There does not appear to be a standard either, as IAM and RDS (both using the "query" protocol) differ in their behavior with respect to returning (or not returning) empty lists.
I am not in favor of altering the model, i.e. setting tags to None when the list is empty. This is a serialization concern and should be handled in the responses.py layer. The other suggestion of copying the model object and manually setting tags to None for the response would probably be fine as a one-off, but I think there's a larger issue here. I'm already working on a PR for IAM to encapsulate the logic for url-encoding policies in responses -- something similar looks like it will be needed for tags. I see a number of botocore/boto3 issues related to tags not being present in IAM reponses (e.g. https://github.com/boto/boto3/issues/1855), so having the logic in one place will be helpful as we work toward parity with AWS.
I will get something for you to look at soon (since I'm the one who made the breaking change and am most familiar with the new serialization code). I will incorporate the tests from this PR. How does that sound?
I will get something for you to look at soon (since I'm the one who made the breaking change and am most familiar with the new serialization code). I will incorporate the tests from this PR. How does that sound?
Sounds good! Appreciate you looking into this @bpandola :pray:
Other attributes that seem to be affected:
IsTruncatedfield of ListGropusForUserDescriptionfield of CreatePolicy
@viren-nadkarni @bblommers Have a look at #9018 and let me know what you think. Thanks.
Superseded by #9018