I'm sure we all have looked at the AWS Console, used a feature and thought "I'd like to automate this process", only to find out that the API calls you are looking for do not exist in the AWS SDKs (boto3, aws-sdk etc). This may be frustrating as you are left 3 not-so-great alternatives.
Sometimes, the feature you are looking for is only a quality of life improvement and you are able to wait.. But other times this action needs to be performed regularly and at scale and performing click-ops becomes problematic.
There was a tweet by @gergnz a few months ago to see if anyone knew whether you can determine your AWS support level via any of the exposed AWS APIs. The general consensus from the community and AWS Support seemed to agree that this is was not yet doable. I took this on as a challenge and came up with 2 methods to achieve this.
I've release an open-source CLI tool to retrieve the information of your AWS support levels across an entire org, a list of AWS Account IDs or the current AWS account. To use the tool, visit my GitHub Repository - sktan/aws-support-level.
After a bit of research, I've found that after making an AWS Support Describe Severity Levels API call, you will receive 2 critical pieces of information:
SubscriptionRequiredException
error)This provides just enough information to determine your support subscription as if you have a look at the Choosing a Severity section in the AWS Support User Guide, it provides a table of what support subscriptions gives you in terms of support levels. To summarise the relevant information:
Severity Level | Support Plan |
---|---|
low | Developer, Business, Enterprise On-Ramp, or Enterprise Support |
normal | Developer, Business, Enterprise On-Ramp, or Enterprise Support |
high | Business, Enterprise On-Ramp, or Enterprise Support |
urgent | Business, Enterprise On-Ramp or Enterprise Support |
critical | Enterprise Support |
With this table, I can determine which level of support I have access to based on the API results and when putting this together, the code looked like this:
import boto3
def get_support_severity_levels():
client = boto3.client(
service_name="support", region_name="us-east-1"
)
try:
response = client.describe_severity_levels(language="en")
severity_levels = []
for severity_level in response["severityLevels"]:
severity_levels.append(severity_level["code"])
except client.exceptions.ClientError as err:
if err.response["Error"]["Code"] == "SubscriptionRequiredException":
return []
raise err
return severity_levels
__SUPPORT_LEVELS__ = {
"critical": "ENTERPRISE",
"urgent": "BUSINESS",
"high": "BUSINESS",
"normal": "DEVELOPER",
"low": "DEVELOPER",
}
support_levels = get_support_severity_levels()
found = False
for level, support_level in __SUPPORT_LEVELS__.items():
if level in support_levels:
found = True
print(f"Your AWS support level is: {support_level}")
break
if not found:
print("Your AWS support level is: BASIC")
The IAM permissions required for this to work:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "support:DescribeSeverityLevels",
"Resource": "*"
}
]
}
This is not ideal as it only attempts to guess based on the support level and will also incorrectly classify "Enterprise On-Ramp" as Business.
This wasn't enough for me though, I decided to navigate the AWS Console and try figure out how the AWS Support Console provides this information to you. When I visited the "Change Support Plans" page, I noticed in my Google Chrome developer tools network tab that there was a XHR call to a promising looking endpoint, https://service.supportplans.us-east-2.api.aws/v1/getSupportPlan
. Upon looking a bit closer at the API request headers, I saw that the call was made using headers following the AWS Signature V4 signing process. This was good news, as that means I can craft a request to this API just by generating following the same signature signing process.
When looking at the AWS Python example that they provide, I saw that it was a full-blown example of how to go through the signing process. But this also incorrectly assumed that I have the following environment variables handy:
I am using AWS profiles with assumed roles, so I wasn't feeling like building an AWS credentials resolver just for this purpose. After digging through some of the open-sourced AWS projects, I found that some of their tools use the AWS CRT Python library which helps with the credentials resolution process and also provides an interface to generate a signed HTTP request with the required headers for authentication / authorisation.
The documentation only provided class / method definitions and assumed you had prior-knowledge on how to use it. After a bit of reverse engineering, I was able to generate a proper request to the API.
For this to work, I found the minimum pieces of information required for a successful API request:
# https://awslabs.github.io/aws-crt-python/api/http.html#awscrt.http.HttpRequest
from awscrt.http import HttpRequest
http_request = HttpRequest(method="GET", path="/v1/getSupportPlan")
http_request.headers.add("Host", "service.supportplans.us-east-2.api.aws")
Once you have this information, you will need to go through the Signature V4 signing process.
# https://awslabs.github.io/aws-crt-python/api/auth.html
from awscrt.auth import (
AwsCredentialsProvider,
AwsSignatureType,
AwsSigningAlgorithm,
AwsSigningConfig,
aws_sign_request,
)
result: HttpRequest = aws_sign_request(
http_request=http_request,
signing_config=AwsSigningConfig(
algorithm=AwsSigningAlgorithm.V4,
signature_type=AwsSignatureType.HTTP_REQUEST_HEADERS,
credentials_provider=AwsCredentialsProvider.new_default_chain(),
service="supportplans",
region="us-east-2",
),
).result()
After signing your HTTP request, you are then able to make your HTTP call to your desired API endpoint:
import requests
response = requests.get(
url="https://service.supportplans.us-east-2.api.aws/v1/getSupportPlan",
headers=dict(result.headers),
)
print(
f"Your AWS support level is: {response.json()['supportPlan']}"
)
The IAM permissions required for this to work:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "supportplans:GetSupportPlan",
"Resource": "*"
}
]
}
At the moment, the code will only work for the current account you are authenticated against and I've yet to implement cross-account roles support.