I'm trying to write a script that keeps the available Bedrock models in sync across multiple accounts (primarily to be sure that we have the same models available in DR as we do in production).
I've figured out how to check for and submit the use case (see this question https://stackoverflow.com/a/79717632/3990127) but there are two other flags that need to be flipped.
A call to aws bedrock get-foundation-model-availability --model-id anthropic.claude-3-haiku-20240307-v1:0 --region eu-west-1 --profile prod
returns a data structure such as:
{
"modelId": "anthropic.claude-3-haiku-20240307-v1",
"agreementAvailability": {
"status": "NOT_AVAILABLE"
},
"authorizationStatus": "AUTHORIZED",
"entitlementAvailability": "NOT_AVAILABLE",
"regionAvailability": "AVAILABLE"
}
In a model that I've signed up for via the console, both agreementAvailability
and entitlementAvailability
are AVAILABLE
.
I've figured out that some APIs which were recently made public include ListFoundationModelAgreementOffers
and CreateFoundationModelAgreement
. These can be used to flip agreementAvailability
by getting an offer token from the list endpoint and using it to create an agreement by submitting the token to the create endpoint (this programatically agrees a set of terms and conditions which are available from the list endpoint as a PDF).
This leaves me with the entitlementAvailability
and I've not been able to figure out how to flip that. AWS support are suggesting starting a change set in the market place, but I can't figure out how to get a product ID from the model ID.
After a lot of experimentation and reverse engineering the console I figured out how to do this.
There is an undocumented API, `FoundationModelEntitlement`, that is key to flipping the other field.
It's a very simple API that lives at https://bedrock.{region}.amazonaws.com/foundation-model-entitlement
. It takes a POST with a body of {'modelId': model_id}
.
I think that this API should have been included in those that they made public in June '25. Hopefully it will be added soon - I've asked for a product feature request to be created for exactly that.
FWIW, my test python call to this looks like this (just using boto3 dependencies):
try:
session = boto3.Session(profile_name=environment)
credentials = session.get_credentials()
# Prepare the request data
url = f'https://bedrock.{region}.amazonaws.com/foundation-model-entitlement'
payload = json.dumps({'modelId': model_id})
print(f"Making request to: {url}")
print(f"Payload: {payload}")
# Create a simple AWSRequest with minimal headers
request = AWSRequest(
method='POST',
url=url,
data=payload,
headers={'Content-Type': 'application/x-amz-json-1.1'}
)
# Sign the request
SigV4Auth(credentials, 'bedrock', region).add_auth(request)
print(f"Request headers after signing: {dict(request.headers)}")
# Convert to urllib request
urllib_request = urllib.request.Request(
url,
data=request.body,
headers=dict(request.headers),
method='POST'
)
# Make the request
try:
with urllib.request.urlopen(urllib_request, timeout=30) as response:
status_code = response.status
response_headers = dict(response.headers)
content = response.read().decode('utf-8')
print(f"Response status: {status_code}")
print(f"Response headers: {response_headers}")
print(f"Response content: {content}")
try:
response_json = json.loads(content) if content else {}
return {"status": "success", "response": response_json, "http_status": status_code}
except json.JSONDecodeError:
return {"status": "success", "response": content, "http_status": status_code}
except urllib.error.HTTPError as http_error:
status_code = http_error.status
error_content = http_error.read().decode('utf-8')
print(f"HTTP Error {status_code}: {error_content}")
return {"status": "http_error", "http_status": status_code, "error": error_content}
except Exception as e:
print(f"✗ Error calling entitlement API: {str(e)}")
return {"status": "error", "error": str(e)}