samcli/lib/utils/arn_utils.py (37 lines of code) (raw):
"""
Module for utilities for ARN (Amazon Resource Names)
"""
import re
class InvalidArnValue(ValueError):
pass
class ARNParts:
"""
Decompose a given ARN into its parts https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html
Attributes
----------
partition: str
the partition part(AWS, aws-cn or aws-us-gov) of the ARN
service: str
the service part(S3, IAM, ECR, ...etc) of the ARN
region: str
the AWS region part(us-east-1, eu-west-1, ...etc) of the ARN
account-id: str
the account-id part of the ARN
resource-id: str
the resource-id part of the ARN
resource-type: str
the resource-type part of the ARN
"""
partition: str
service: str
region: str
account_id: str
resource_type: str
resource_id: str
def __init__(self, arn: str) -> None:
# Regex pattern formed based on the 3 ARN general formats found here:
# https://docs.aws.amazon.com/IAM/latest/UserGuide/reference-arns.html
arn_pattern = (
r"arn:([a-zA-Z0-9_-]+):" # Pattern for partition
r"([a-zA-Z0-9_-]+):" # Pattern for service
r"([a-zA-Z0-9_-]*):" # Pattern for region
r"([a-zA-Z0-9_-]*)" # Pattern for account_id
r"(?::([a-zA-Z0-9_-]+))?" # Pattern for resource_type if it exists
r"(?::(.+))" # Pattern for resource_id if it exists
)
matched_arn = re.match(arn_pattern, arn)
if not matched_arn:
raise InvalidArnValue(f"Invalid ARN ({arn}) provided")
self.partition = matched_arn.group(1)
self.service = matched_arn.group(2)
self.region = matched_arn.group(3) if matched_arn.group(3) else ""
self.account_id = matched_arn.group(4) if matched_arn.group(4) else ""
self.resource_type = matched_arn.group(5) if matched_arn.group(5) else ""
if matched_arn.group(5):
# This handles the Arns of services with the format:
# arn:partition:service:region:account-id:resource-type:resource-id
self.resource_type = matched_arn.group(5)
self.resource_id = matched_arn.group(6) if matched_arn.group(6) else ""
elif "/" in matched_arn.group(6):
# This handles the Arns of services with the format:
# arn:partition:service:region:account-id:resource-type/resource-id
split_resource_type_and_id = matched_arn.group(6).split("/", 1)
self.resource_type = split_resource_type_and_id[0]
self.resource_id = split_resource_type_and_id[1]
else:
# This handles the Arns of services with the format:
# arn:partition:service:region:account-id:resource-id
self.resource_type = ""
self.resource_id = matched_arn.group(6) if matched_arn.group(6) else ""