65

Problem:

I'm trying to get the region of the authenticated user from boto3.

Use case:

I'm working on adding cache to https://github.com/pmazurek/aws-fuzzy-finder. I would prefer to cache the result on per-region basis.

This package uses boto to get user authentication data (keys and the region). The problem is the region is never passed explicitly by the user, its being taken from one of the many murky places that boto reads so I don't really have a way of getting it.

I have tried searching through boto3 api and googling, but couldn't find anything like a get_region or get_user_data method. Is it possible?

Piotr Mazurek
  • 1,083
  • 1
  • 8
  • 14

4 Answers4

95

You should be able to read the region_name from the session.Session object like

my_session = boto3.session.Session()
my_region = my_session.region_name

region_name is basically defined as session.get_config_variable('region')

Frederic Henri
  • 51,761
  • 10
  • 113
  • 139
  • 4
    Just in case someone else needs this in future, you need to instantiate the session: `sess = boto3.session.Session(); sess.region_name` – Piotr Mazurek May 30 '16 at 17:07
  • 28
    This won't work if `AWS_DEFAULT_REGION` isn't set or your region_name isn't configured in `~/.aws/config` – RyPeck Mar 08 '18 at 14:22
52

Another option, if you are working with a boto3 client, is:

import boto3
client = boto3.client('s3') # example client, could be any
client.meta.region_name
Steven Miller
  • 806
  • 7
  • 4
  • 10
    not working for current instance, it just returns "us-east-1" from any server – mustafagok Jan 02 '20 at 14:18
  • Some clients are probably only in us-east-1; I'd check what client you're using. I just checked and indeed s3 puts you in region us-east-1. Also, if region isn't set you get an exception. – J. Gwinner Jan 19 '23 at 18:39
19

Took some ideas from here and other posts, and I believe this should work for pretty much any setup, whether local or on any AWS service including Lambda, EC2, ECS, Glue, etc:

def detect_running_region():
    """Dynamically determine the region from a running Glue job (or anything on EC2 for
    that matter)."""
    easy_checks = [
        # check if set through ENV vars
        os.environ.get('AWS_REGION'),
        os.environ.get('AWS_DEFAULT_REGION'),
        # else check if set in config or in boto already
        boto3.DEFAULT_SESSION.region_name if boto3.DEFAULT_SESSION else None,
        boto3.Session().region_name,
    ]
    for region in easy_checks:
        if region:
            return region

    # else query an external service
    # https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-identity-documents.html
    r = requests.get("http://169.254.169.254/latest/dynamic/instance-identity/document")
    response_json = r.json()
    return response_json.get('region')
aiguofer
  • 1,887
  • 20
  • 34
  • 1
    You can actually iterate over lines of code ?!? Python never ceases to amaze me... – ssc Apr 29 '22 at 14:04
  • @ssc it's not technically iterating over lines of code.... each of those lines is returning a value (either a str or None), so `easy_checks` is just a list of strings or None values. The iteration is just finding the first None value – aiguofer Apr 29 '22 at 18:12
  • It sure does look like it ;-) Thanks for the clarification :+1: I rephrase my comment to "You can create a list of lines of code ?!?" and still find that pretty amazing... – ssc Apr 30 '22 at 09:15
  • @sec ahh yeah :) python is so flexible and readable.... I love all of the list/dict comprehension and value unpacking, they help write extremely readable code! – aiguofer May 02 '22 at 03:50
2

None of these worked for me, as AWS_DEFAULT_REGION is not setup and client.meta.region_name gives 'us-east-1' and I don't want to use URLs. If there is already a bucket set up in that region and you are already accessing it using boto3 (note, you don't need region to access s3) then below works (as at Aug'20).

import boto3
client = boto3.client('s3')
response = client.get_bucket_location(Bucket=bucket_name)
print(response['LocationConstraint'])
Lavesh
  • 169
  • 7