IAM resource classes are incompatible with root sessions
Describe the bug
Suppose we have a boto3 session object corresponding to a root user. Let's name it root_session:
root_user = root_session.resource('iam').CurrentUser()
I've noticed two different behaviors:
-
root_user.user_name is None: this happens because, for reasons not clear to me (might have to do with lack of account alias), the underlying response fromGetUserdoes not contain anUserNamefield. Sinceroot_user.useris alsoNone, it seems as if there's no way to delete the root user's login profile from this resource class. -
root_user.user_name is not None: this doesn't work out either. Theuser_namevalue may be passed down to API calls which expect no username for root users. AFAICT, any explicit username value would be considered that of an IAM user. Due to the strict session policies for root tasks, you get something like this:next(root_user.user.access_keys.all()) Traceback (most recent call last): ... botocore.exceptions.ClientError: An error occurred (AccessDenied) when calling the ListAccessKeys operation: User: arn:aws:iam::123412341234:root is not authorized to perform: iam:ListAccessKeys on resource: user myAccountUserName with an explicit deny in an identity-based policy
Regression Issue
- [ ] Select this option if this issue appears to be a regression.
Expected Behavior
In either case outlined above, I expect a functional CurrentUser object that may be used to audit or delete root user credentials depending on the session policy in effect.
Current Behavior
Please see bug description.
Reproduction Steps
Please see bug description.
Possible Solution
No response
Additional Information/Context
No response
SDK version used
1.37.9
Environment details (OS name and version, etc.)
cpe:/o:fedoraproject:fedora:41
Hello @dhx-mike-palandra, thanks for raising this. What you're observing is expected behavior due to how AWS IAM handles root users differently from IAM users. Specifically, the boto3.resource('iam').CurrentUser() method assumes that the calling identity is a regular IAM user and attempts to use the UserName field in subsequent calls — like list_access_keys. However, the root user is not an IAM user, and therefore does not have a UserName value associated with it: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_root-user.html.
In contrast, IAM users are created with a unique UserName used for identification in IAM operations: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users.html.
When boto3 sends a UserName in the ListAccessKeys request while using root credentials, IAM interprets this as a request to retrieve keys for an IAM user — not the root identity. Because the root user doesn’t correspond to any IAM user, this results in an AccessDenied error. IAM expects no UserName to be specified when retrieving access keys for the root user. This behavior is documented in the ListAccessKeys API reference, which states:
"If the UserName is not specified, the user name is determined implicitly based on the AWS access key ID used to sign the request." https://docs.aws.amazon.com/IAM/latest/APIReference/API_ListAccessKeys.html
Could also use boto3.client('iam') with conditional root detection) provides the same auditing capability you'd expect from CurrentUser() — including safely listing access keys or managing credentials when permissions allow — without breaking on root-specific edge cases.
import boto3
from botocore.exceptions import ClientError
def list_my_access_keys():
iam = boto3.client("iam")
try:
user = iam.get_user()
arn = user["User"]["Arn"]
user_name = user["User"].get("UserName")
print("Current identity ARN:", arn)
if arn.endswith(":root"):
print("Running as root user — skipping access key listing (must omit UserName).")
try:
root_keys = iam.list_access_keys()
print("Root access keys:")
for key in root_keys["AccessKeyMetadata"]:
print("Key", key["AccessKeyId"])
except ClientError as e:
print("Root user access key listing failed:", e.response["Error"]["Message"])
else:
print("IAM User:", user_name)
response = iam.list_access_keys(UserName=user_name)
print("Access keys for IAM user:")
for key in response["AccessKeyMetadata"]:
print("Key", key["AccessKeyId"])
except ClientError as e:
print("ClientError:", e.response["Error"]["Message"])
list_my_access_keys()
Thanks very much @adev-code for your thorough response and sample code.
I too noticed that API calls which may apply to root users/sessions (such as ListAccessKeys) always determine the root user implicitly. I solved my problem with botocore instead due to this limitation with the boto3 IAM resource class.
Feel free to close this issue as you see fit.
BTW, I still consider this a bug:
-
Calling
CurrentUser()from a root user/session returns a non-None object without raising a runtime error. -
For root/user sessions, both
CurrentUser()andlist_access_keys()determine their subject implicitly by access ID of caller. Despite this symmetry,CurrentUser().access_keys.all()always raises a runtime error in this case whereaslist_access_keys()does not. Likewise with other resource collections fromCurrentUser.
Initially, I thought this could be fixed by some resource model tweaks only. Now, I suspect that a workable solution likely would utilize the event system too.
Perhaps my previous comment was a bit hasty. Sorry about that.
Take care.
Thanks for the reply. Are you using resource or client? In regards to resources, this feature is feature frozen and we recommend using client:
The AWS Python SDK team does not intend to add new features to the resources interface in boto3. Existing interfaces will continue to operate during boto3’s lifecycle. Customers can find access to newer service features through the client interface.
Initially, I was using boto3 resource classes until I discovered this issue, then I switched to botocore.
Thanks for pointing out the feature freeze on the resource interface. In future projects, I will avoid using it.
This issue is now closed. Comments on closed issues are hard for our team to see. If you need more assistance, please open a new issue that references this one.