Skip to main content

aws_common/
client.rs

1//! Helper functions to create AWS SDK clients from configuration.
2
3use crate::config::AwsConfig;
4use aws_config::BehaviorVersion;
5use aws_config::meta::region::RegionProviderChain;
6use aws_config::sts::AssumeRoleProvider;
7use aws_credential_types::Credentials;
8use aws_sdk_kinesis::Client as KinesisClient;
9use aws_sdk_s3::Client as S3Client;
10use aws_sdk_sqs::Client as SqsClient;
11use snafu::Snafu;
12use std::time::Duration;
13
14#[derive(Debug, Snafu)]
15pub enum Error {
16    #[snafu(display("Failed to load AWS configuration: {}", source))]
17    ConfigLoad { source: std::io::Error }, // Placeholder error
18}
19
20pub type Result<T, E = Error> = std::result::Result<T, E>;
21
22/// Loads common AWS configuration from environment and overrides.
23pub async fn load_config(config: &AwsConfig) -> aws_config::SdkConfig {
24    let region_provider =
25        RegionProviderChain::first_try(config.region.clone().map(aws_config::Region::new))
26            .or_default_provider()
27            .or_else(aws_config::Region::new("us-east-1"));
28
29    let mut loader = aws_config::defaults(BehaviorVersion::latest()).region(region_provider);
30
31    if let Some(endpoint) = &config.endpoint {
32        loader = loader.endpoint_url(endpoint);
33    }
34
35    // Handle authentication methods
36    if let Some(access_key) = &config.access_key_id {
37        tracing::warn!(
38            "AWS credentials (access_key_id/secret_access_key) are explicitly set. For better security, consider using IAM roles, instance profiles, or injected environment variables."
39        );
40        if let Some(secret_key) = &config.secret_access_key {
41            let creds = Credentials::new(
42                access_key.clone(),
43                secret_key.clone(),
44                config.session_token.clone(),
45                None,
46                "manual",
47            );
48            loader = loader.credentials_provider(creds);
49        }
50    } else if let Some(profile) = &config.profile {
51        let profile_builder =
52            aws_config::profile::ProfileFileCredentialsProvider::builder().profile_name(profile);
53        loader = loader.credentials_provider(profile_builder.build());
54    } else if let Some(imds_config) = &config.imds {
55        let mut imds_client_builder = aws_config::imds::client::Client::builder();
56        if let Some(timeout) = imds_config.connect_timeout_seconds {
57            imds_client_builder = imds_client_builder.connect_timeout(Duration::from_secs(timeout));
58        }
59        if let Some(timeout) = imds_config.read_timeout_seconds {
60            imds_client_builder = imds_client_builder.read_timeout(Duration::from_secs(timeout));
61        }
62
63        let imds_provider = aws_config::imds::credentials::ImdsCredentialsProvider::builder()
64            .imds_client(imds_client_builder.build())
65            .build();
66
67        loader = loader.credentials_provider(imds_provider);
68    }
69
70    let mut sdk_config = loader.load().await;
71
72    // Handle AssumeRole
73    if let Some(role_arn) = &config.assume_role {
74        let mut role_builder = AssumeRoleProvider::builder(role_arn).configure(&sdk_config);
75
76        if let Some(session_name) = &config.session_name {
77            role_builder = role_builder.session_name(session_name);
78        }
79        if let Some(external_id) = &config.external_id {
80            role_builder = role_builder.external_id(external_id);
81        }
82
83        let role_provider = role_builder.build().await;
84
85        // Re-load config with the new credentials provider
86        sdk_config = aws_config::defaults(BehaviorVersion::latest())
87            .region(sdk_config.region().cloned())
88            .credentials_provider(role_provider)
89            .load()
90            .await;
91    }
92
93    sdk_config
94}
95
96/// Creates an S3 client.
97pub async fn create_s3_client(config: &AwsConfig) -> S3Client {
98    let sdk_config = load_config(config).await;
99    S3Client::new(&sdk_config)
100}
101
102/// Creates an SQS client.
103pub async fn create_sqs_client(config: &AwsConfig) -> SqsClient {
104    let sdk_config = load_config(config).await;
105    SqsClient::new(&sdk_config)
106}
107
108/// Creates a Kinesis client.
109pub async fn create_kinesis_client(config: &AwsConfig) -> KinesisClient {
110    let sdk_config = load_config(config).await;
111    KinesisClient::new(&sdk_config)
112}