How to Programmatically Identify the AWS Account Owner of an S3 Bucket Using API Calls


2 views

When working with multi-account AWS environments, a common challenge arises when you encounter an S3 object URL (e.g., s3://example-bucket/path/to/object) but don't know which of your hundreds of AWS accounts has permissions to access it. Here's how to systematically identify the owning account.

The most reliable methods to identify bucket ownership:

import boto3
from botocore.exceptions import ClientError

def find_bucket_owner(bucket_name, account_profiles):
    for profile in account_profiles:
        try:
            session = boto3.Session(profile_name=profile)
            s3 = session.client('s3')
            result = s3.get_bucket_acl(Bucket=bucket_name)
            return {
                'account_id': result['Owner']['ID'],
                'account_profile': profile
            }
        except ClientError as e:
            if e.response['Error']['Code'] != 'AccessDenied':
                continue
    return None
  1. First attempt read access with your default credentials
  2. If denied, iterate through your available AWS profiles/accounts
  3. Use the get_bucket_acl or get_bucket_location API calls
  4. Analyze response headers for ownership clues

For organizations with many accounts, consider these optimizations:

# Parallel account checking using GNU parallel
parallel -j 10 "aws s3api get-bucket-acl --bucket target-bucket \
--profile {} 2>&1 | grep -q 'Owner' && echo {}" ::: $(aws-profiles)

Key observations from response headers:

  • x-amz-owner-id in API responses
  • Account ID patterns in error messages
  • Bucket naming conventions (if standardized)

When API access isn't available:

# Check via S3 console URL patterns
console_url = f"https://s3.console.aws.amazon.com/s3/buckets/{bucket_name}"
# The URL will redirect to the owning account's console

Remember that bucket ownership transfer between accounts is complex and requires specific procedures.

Always ensure you have proper authorization before attempting to access buckets. This process should only be used:

  • With legitimate cross-account permissions
  • On buckets your organization owns
  • For legitimate administrative purposes

When working with distributed AWS environments, a common scenario arises: you receive an S3 object URL (e.g., s3://bucket-name/path/to/object) but don't know which AWS account owns the bucket. This becomes critical when you need to assume the correct IAM role across hundreds of potential accounts.

Here are three reliable methods to determine bucket ownership:

1. Using AWS CLI with HeadBucket

aws s3api head-bucket --bucket bucket-name

This will return the bucket's Region and account information if you have permissions. For cross-account scenarios:

{
    "ResponseMetadata": {
        "HTTPHeaders": {
            "x-amz-bucket-region": "us-east-1",
            "x-amz-id-2": "EXAMPLEID",
            "x-amz-request-id": "EXAMPLEID"
        }
    }
}

2. Bucket Policy Inspection via API

The most definitive method is checking the bucket policy:

aws s3api get-bucket-policy --bucket bucket-name

Successful response will include the owner's account ID in the policy document:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::123456789012:root"
            },
            ...
        }
    ]
}

3. Resource Tagging Strategy

For organizations with proper tagging conventions:

aws s3api get-bucket-tagging --bucket bucket-name

Example response showing owner account:

{
    "TagSet": [
        {
            "Key": "OwnerAccount",
            "Value": "123456789012"
        }
    ]
}

For bulk operations across multiple accounts, use AWS Organizations with the following Python script:

import boto3

def find_bucket_owner(bucket_name):
    org_client = boto3.client('organizations')
    accounts = org_client.list_accounts()
    
    for account in accounts['Accounts']:
        sts_client = boto3.client('sts')
        assumed_role = sts_client.assume_role(
            RoleArn=f"arn:aws:iam::{account['Id']}:role/OrganizationAccountAccessRole",
            RoleSessionName="BucketDiscovery"
        )
        
        s3_client = boto3.client(
            's3',
            aws_access_key_id=assumed_role['Credentials']['AccessKeyId'],
            aws_secret_access_key=assumed_role['Credentials']['SecretAccessKey'],
            aws_session_token=assumed_role['Credentials']['SessionToken']
        )
        
        try:
            s3_client.head_bucket(Bucket=bucket_name)
            return account['Id']
        except:
            continue
    
    return None
  • Ensure your base role has organizations:ListAccounts permission
  • Limit the assumed role permissions to read-only S3 operations
  • Implement proper error handling for permission denied scenarios

For organizations using AWS RAM, you can check shared resource associations:

aws ram get-resource-share-associations \
    --association-type RESOURCE \
    --resource-arn arn:aws:s3:::bucket-name