Problem
When attempting to create a serverless budget policy using the workspace-level API, you receive a 404 error.
ERROR: Failed to create budget policy: 404
Endpoint not found
Cause
The API endpoint used is not correct or accessible for workspace-level policy creation.
Solution
If you want to create a serverless budget policy at the workspace level, use the UI. For details, refer to the “Create a serverless budget policy” section of the Attribute usage with serverless budget policies (AWS | Azure | GCP) documentation.
To continue using an API to create a serverless budget policy, do so at the account-level instead. For more information, review the Authorizing access to Databricks resources (AWS | Azure | GCP) documentation.
Then, you can use the billing admin role to access the policy in your workspace.
Account-level API example
First, define your account console endpoint based on your cloud platform.
- (AWS)
cloud.databricks.com
- (Azure)
azuredatabricks.net
- (GCP)
gcp.databricks.com
Next, create your budget policy.
POST
https://<account-console-endpoint>/api/2.1/accounts/<account-id>/budget-policies
For more information about the Budget policy account console API, review the Create a budget policy (AWS | Azure | GCP) API documentation.
Then, update your budget policy rules to provide specific permissions.
PUT
https://<account-console-endpoint>/api/2.0/preview/accounts/<account-id>/access-control/rule-sets
For more information about updating a budget policy rule, review the Update a rule set (AWS | Azure | GCP) API documentation.
Implementation example in Python
The following code example
- Creates a budget policy.
- Gets the policy ID and policy etag to perform changes in the budget policy.
- Defines users to assign permissions for the budget policy.
- Updates policy rules.
import requests
api_key = "<your-token>"
host = "https://<account-console-endpoint>"
account_id = "<your-account-id>"
head = {
"Authorization" : "Bearer " + api_key
}
def create_budget_policy(name):
create = requests.post(f"{host}/api/2.1/accounts/{account_id}/budget-policies", json={
"policy_name" : name,
"custom_tags" : [{
"key" : "tag1", "value" : "val1"
}, {
"key" : "tag2", "value" : "val2"
}]
}, headers=head)
return create.json()["policy_id"] #Add error handling as required
def get_policy_id_from_policy_name(name):
try:
get_name = requests.get(f"{host}/api/2.1/accounts/{account_id}/budget-policies", headers=head)
policy_id = None
for policy in get_name.json()['policies']:
if policy['policy_name'] == name:
policy_id = policy['policy_id']
return policy_id
except Exception as e:
return None
def get_etag(policy_id):
etag = requests.get(f"{host}/api/2.1/preview/accounts/{account_id}/access-control/rule-sets", params={
"name" : f"accounts/{account_id}/budgetPolicies/{policy_id}/ruleSets/default",
"etag" : ""
}, headers=head)
return etag.json()['etag']
def get_req_body():
#take below details as parameter in actual script, this is demo script
users = ["user1@example.com", "user2@example.com"] #Will by default give user role
service_principal = ["00000-0000-0000-0000-000000", "111111-1111-1111-1111-111111"] #Will give manager role. Please update the script if different service principal need to have different role.
groups = ["group_example_name"]
data = []
for u in users:
data.append({
"role" : "roles/budgetPolicy.user",
"principals" : [
f"users/{u}"
]
})
for s in service_principal:
data.append({
"role" : "roles/budgetPolicy.manager",
"principals" : [
f"servicePrincipals/{s}"
]
})
for g in groups:
data.append({
"role" : "roles/budgetPolicy.manager",
"principals" : [
f"groups/{g}"
]
})
return data
def update_budget_rules(policy_id, etag):
print({
"name" : f"accounts/{account_id}/budgetPolicies/{policy_id}/ruleSets/default",
"rule_set" : {
"name" : f"accounts/{account_id}/budgetPolicies/{policy_id}/ruleSets/default",
"description" : "",
"grant_rules" : get_req_body(),
"etag" : etag
}})
assign = requests.put(f"{host}/api/2.1/preview/accounts/{account_id}/access-control/rule-sets", json={
"name" : f"accounts/{account_id}/budgetPolicies/{policy_id}/ruleSets/default",
"rule_set" : {
"name" : f"accounts/{account_id}/budgetPolicies/{policy_id}/ruleSets/default",
"description" : "",
"grant_rules" : get_req_body(),
"etag" : etag
}}, headers=head)
return None
if __name__ == "__main__":
policy_name = "<your_policy_name>"
policy_id = get_policy_id_from_policy_name(policy_name)
policy_id = create_budget_policy(policy_name) if not policy_id else policy_id
etag = get_etag(policy_id=policy_id)
update_budget_rules(policy_id=policy_id, etag=etag)