Source code for ds_provider_aws_py_lib.linked_service.aws
"""
**File:** ``aws.py``
**Region:** ``ds_provider_aws_py_lib/linked_service/aws``
AWS Linked Service
This module implements a linked service for AWS.
"""
from dataclasses import dataclass, field
from typing import Generic, TypeVar
import boto3
from botocore.exceptions import ClientError
from ds_common_logger_py_lib import Logger
from ds_resource_plugin_py_lib.common.resource.linked_service import LinkedService, LinkedServiceSettings
from ds_resource_plugin_py_lib.common.resource.linked_service.errors import AuthorizationError, ConnectionError
from ..enums import ResourceType
logger = Logger.get_logger(__name__, package=True)
[docs]
@dataclass(kw_only=True)
class AWSLinkedServiceSettings(LinkedServiceSettings):
"""
The object containing the AWS linked service settings.
"""
account_id: str
"""The AWS account ID."""
access_key_id: str
"""The AWS access key ID."""
access_key_secret: str = field(metadata={"mask": True})
"""The AWS access key secret."""
region: str = "eu-north-1"
"""The AWS region."""
AWSLinkedServiceSettingsType = TypeVar(
"AWSLinkedServiceSettingsType",
bound=AWSLinkedServiceSettings,
)
[docs]
@dataclass(kw_only=True)
class AWSLinkedService(LinkedService[AWSLinkedServiceSettingsType], Generic[AWSLinkedServiceSettingsType]):
"""
The class is used to connect with AWS services.
"""
settings: AWSLinkedServiceSettingsType
_connection: boto3.Session | None = field(default=None, init=False, repr=False, metadata={"serialize": False})
@property
def type(self) -> ResourceType:
"""
Get the type of the linked service.
Returns:
ResourceType
"""
return ResourceType.LINKED_SERVICE
@property
def connection(self) -> boto3.Session:
"""
Get the connection to AWS.
Returns:
boto3.Session: The established boto3 session.
"""
if self._connection is None:
raise ConnectionError("No AWS session available. Call connect() first.")
return self._connection
[docs]
def connect(self) -> None:
"""
Create a boto3 session using the provided AWS credentials and verify the account ID if specified.
Raises:
AuthorizationError: If the AWS account ID does not match the expected value.
"""
logger.debug(
"Connecting to AWS account_id=%s in region=%s",
self.settings.account_id,
self.settings.region,
)
session = boto3.Session(
aws_account_id=self.settings.account_id,
region_name=self.settings.region,
aws_access_key_id=self.settings.access_key_id,
aws_secret_access_key=self.settings.access_key_secret,
)
sts_client = session.client("sts")
try:
identity = sts_client.get_caller_identity()
actual_account_id = identity.get("Account")
except ClientError as exc:
logger.error("Unable to verify AWS account ID: %s", exc)
raise AuthorizationError(
message="Unable to verify AWS account ID.",
details={
"type": self.type.value,
"expected_account_id": self.settings.account_id,
},
) from exc
if actual_account_id != self.settings.account_id:
raise AuthorizationError(
message=f"Unable to verify AWS account ID. "
f"{actual_account_id} does not match expected value: {self.settings.account_id}",
details={
"type": self.type.value,
"expected_account_id": self.settings.account_id,
"actual_account_id": actual_account_id,
},
)
self._connection = session
[docs]
def test_connection(self) -> tuple[bool, str]:
"""
Test the connection to AWS by creating the session.
Returns:
tuple[bool, str]: A tuple containing a boolean indicating success and a message.
"""
try:
self.connect()
return True, "Connection successfully tested"
except ClientError as exc:
return False, str(exc)
[docs]
def close(self) -> None:
"""
boto3 sessions do not require explicit closing.
"""
pass