boto3 client NoRegionError: You must specify a region error only sometimes
Question:
I have a boto3 client :
boto3.client('kms')
But it happens on new machines, They open and close dynamically.
if endpoint is None:
if region_name is None:
# Raise a more specific error message that will give
# better guidance to the user what needs to happen.
raise NoRegionError()
Why is this happening? and why only part of the time?
Answers:
One way or another you must tell boto3 in which region you wish the kms
client to be created. This could be done explicitly using the region_name
parameter as in:
kms = boto3.client('kms', region_name='us-west-2')
or you can have a default region associated with your profile in your ~/.aws/config
file as in:
[default]
region=us-west-2
or you can use an environment variable as in:
export AWS_DEFAULT_REGION=us-west-2
but you do need to tell boto3 which region to use.
I believe, by default, boto picks the region which is set in aws cli. You can run command #aws configure and press enter (it shows what creds you have set in aws cli with region)twice to confirm your region.
you can also set environment variables in the script itself, rather than passing region_name parameter
os.environ['AWS_DEFAULT_REGION'] = 'your_region_name'
case sensitivity may matter.
os.environ['AWS_DEFAULT_REGION'] = 'your_region_name'
In my case sensitivity mattered.
For Python 2 I have found that the boto3 library does not source the region from the ~/.aws/config
if the region is defined in a different profile to default.
So you have to define it in the session creation.
session = boto3.Session(
profile_name='NotDefault',
region_name='ap-southeast-2'
)
print(session.available_profiles)
client = session.client(
'ec2'
)
Where my ~/.aws/config
file looks like this:
[default]
region=ap-southeast-2
[NotDefault]
region=ap-southeast-2
I do this because I use different profiles for different logins to AWS, Personal and Work.
For those using CloudFormation template. You can set AWS_DEFAULT_REGION
environment variable using UserData and AWS::Region
. For example,
MyInstance1:
Type: AWS::EC2::Instance
Properties:
ImageId: ami-04b9e92b5572fa0d1 #ubuntu
InstanceType: t2.micro
UserData:
Fn::Base64: !Sub |
#!/bin/bash -x
echo "export AWS_DEFAULT_REGION=${AWS::Region}" >> /etc/profile
Alternatively you can run the following (aws cli)
aws configure --profile $PROFILE_NAME
it’ll prompt you for the region.
notice in ~/.aws/config
it’s:
[default]
region = ap-southeast-1
output = json
[profile prod]
region = ap-southeast-1
output = json
[profile profile name] in the square brackets
If you are using lambdas, then you would likely want to use the region the lambda is deployed in.
You can use the following
import boto3
import json
import os
def lambda_handler(event, context):
region = os.environ['AWS_REGION']
print('Lambda region: ', region)
kms = boto3.client('kms', region_name=region)
We have the configured regions stored in our ~/.aws/config file. Here is a pure python way of reading the correct region from this file based on the profile name:
def get_aws_region(profile_name: str) -> str:
config = configparser.ConfigParser()
config.read(f"{os.environ['HOME']}/.aws/config")
profile_section = config[f"profile {profile_name}"]
return profile_section["region"]
regions = [
'eu-north-1', 'ap-south-1', 'eu-west-3', 'eu-west-2',
'eu-west-1', 'ap-northeast-3', 'ap-northeast-2'
'ap-northeast-1', 'sa-east-1', 'ca-central-1',
'ap-southeast-2', 'eu-central-1', 'us-east-1', 'us-east-2',
'us-west-1', 'us-west-2']
for r in regions:
kms = boto3.client('kms', region_name= r)
If you use AWS Lambda your code will work while you deploy it,
because the Lambda is deployed in a specific region.
you could always set it to the same region that the EC2 is in if you like. This is with bash but you could easily reproduce this in python:
EC2_AVAIL_ZONE=$(curl -s http://169.254.169.254/latest/meta-data/placement/availability-zone)
EC2_REGION="`echo $EC2_AVAIL_ZONE | sed -e 's:([0-9][0-9]*)[a-z]*$:\1:'`"
# if unset, set it:
export AWS_DEFAULT_REGION="${AWS_DEFAULT_REGION:-$EC2_REGION}"
python:
import os
import re
from subprocess import check_output
EC2_AVAIL_ZONE = check_output(["curl", "-s", "http://169.254.169.254/latest/meta-data/placement/availability-zone"]).decode().strip()
EC2_REGION = re.sub(r"^(w{2}-w{3,10}-[0-9][0-9]*)[a-z]*$", r"1", EC2_AVAIL_ZONE)
# if unset, set it:
os.environ["AWS_DEFAULT_REGION"] = os.getenv("AWS_DEFAULT_REGION", EC2_REGION)
In case you are using Linux, an easy way is that you can create the config
file (You should name it config
without any extension) at ~/.aws
(if .aws
directory does not exist create it at user home ~
) and in that config
file add a region just like this.
[default]
region=whatever-aws-region
I have a boto3 client :
boto3.client('kms')
But it happens on new machines, They open and close dynamically.
if endpoint is None:
if region_name is None:
# Raise a more specific error message that will give
# better guidance to the user what needs to happen.
raise NoRegionError()
Why is this happening? and why only part of the time?
One way or another you must tell boto3 in which region you wish the kms
client to be created. This could be done explicitly using the region_name
parameter as in:
kms = boto3.client('kms', region_name='us-west-2')
or you can have a default region associated with your profile in your ~/.aws/config
file as in:
[default]
region=us-west-2
or you can use an environment variable as in:
export AWS_DEFAULT_REGION=us-west-2
but you do need to tell boto3 which region to use.
I believe, by default, boto picks the region which is set in aws cli. You can run command #aws configure and press enter (it shows what creds you have set in aws cli with region)twice to confirm your region.
you can also set environment variables in the script itself, rather than passing region_name parameter
os.environ['AWS_DEFAULT_REGION'] = 'your_region_name'
case sensitivity may matter.
os.environ['AWS_DEFAULT_REGION'] = 'your_region_name'
In my case sensitivity mattered.
For Python 2 I have found that the boto3 library does not source the region from the ~/.aws/config
if the region is defined in a different profile to default.
So you have to define it in the session creation.
session = boto3.Session(
profile_name='NotDefault',
region_name='ap-southeast-2'
)
print(session.available_profiles)
client = session.client(
'ec2'
)
Where my ~/.aws/config
file looks like this:
[default]
region=ap-southeast-2
[NotDefault]
region=ap-southeast-2
I do this because I use different profiles for different logins to AWS, Personal and Work.
For those using CloudFormation template. You can set AWS_DEFAULT_REGION
environment variable using UserData and AWS::Region
. For example,
MyInstance1:
Type: AWS::EC2::Instance
Properties:
ImageId: ami-04b9e92b5572fa0d1 #ubuntu
InstanceType: t2.micro
UserData:
Fn::Base64: !Sub |
#!/bin/bash -x
echo "export AWS_DEFAULT_REGION=${AWS::Region}" >> /etc/profile
Alternatively you can run the following (aws cli)
aws configure --profile $PROFILE_NAME
it’ll prompt you for the region.
notice in ~/.aws/config
it’s:
[default]
region = ap-southeast-1
output = json
[profile prod]
region = ap-southeast-1
output = json
[profile profile name] in the square brackets
If you are using lambdas, then you would likely want to use the region the lambda is deployed in.
You can use the following
import boto3
import json
import os
def lambda_handler(event, context):
region = os.environ['AWS_REGION']
print('Lambda region: ', region)
kms = boto3.client('kms', region_name=region)
We have the configured regions stored in our ~/.aws/config file. Here is a pure python way of reading the correct region from this file based on the profile name:
def get_aws_region(profile_name: str) -> str:
config = configparser.ConfigParser()
config.read(f"{os.environ['HOME']}/.aws/config")
profile_section = config[f"profile {profile_name}"]
return profile_section["region"]
regions = [
'eu-north-1', 'ap-south-1', 'eu-west-3', 'eu-west-2',
'eu-west-1', 'ap-northeast-3', 'ap-northeast-2'
'ap-northeast-1', 'sa-east-1', 'ca-central-1',
'ap-southeast-2', 'eu-central-1', 'us-east-1', 'us-east-2',
'us-west-1', 'us-west-2']
for r in regions:
kms = boto3.client('kms', region_name= r)
If you use AWS Lambda your code will work while you deploy it,
because the Lambda is deployed in a specific region.
you could always set it to the same region that the EC2 is in if you like. This is with bash but you could easily reproduce this in python:
EC2_AVAIL_ZONE=$(curl -s http://169.254.169.254/latest/meta-data/placement/availability-zone)
EC2_REGION="`echo $EC2_AVAIL_ZONE | sed -e 's:([0-9][0-9]*)[a-z]*$:\1:'`"
# if unset, set it:
export AWS_DEFAULT_REGION="${AWS_DEFAULT_REGION:-$EC2_REGION}"
python:
import os
import re
from subprocess import check_output
EC2_AVAIL_ZONE = check_output(["curl", "-s", "http://169.254.169.254/latest/meta-data/placement/availability-zone"]).decode().strip()
EC2_REGION = re.sub(r"^(w{2}-w{3,10}-[0-9][0-9]*)[a-z]*$", r"1", EC2_AVAIL_ZONE)
# if unset, set it:
os.environ["AWS_DEFAULT_REGION"] = os.getenv("AWS_DEFAULT_REGION", EC2_REGION)
In case you are using Linux, an easy way is that you can create the config
file (You should name it config
without any extension) at ~/.aws
(if .aws
directory does not exist create it at user home ~
) and in that config
file add a region just like this.
[default]
region=whatever-aws-region