diff --git a/documentation/configuration.md b/documentation/configuration.md index ec83d834791..f4380f7b6a8 100644 --- a/documentation/configuration.md +++ b/documentation/configuration.md @@ -855,9 +855,11 @@ The Queue Message's payload will be the blob name (`/` |---|---|---|---| | endpoint | string | true | An optional endpoint of S3 storage service. Can be left empty in case of using AWS. | | bucketName | string | true | The name of the s3 Bucket to which the blob will be egressed | -| accountKeyName | string | true | The user credential for accessing the s3 service | -| secretsFile | string | false | Path to a file on disk which holds the user's password for accessing the s3 storage. If not provided the `password` property must be set. | -| accountKey | string | false | The user's password for accessing the s3 storage. If not provided the `SecretsFile` must be specified. | +| accessKeyId | string | true | The AWS AccessKeyId for IAM user to login | +| secretAccessKeyFile | string | false | Path to a file on disk which holds the user's password for accessing the s3 storage. If not provided the `password` property must be set. | +| secretAccessKey | string | false | The AWS SecretAccessKey associated AccessKeyId for IAM user to login. | +| awsProfileName | string | false | The AWS profile name to be used for login. | +| awsProfilePath | string | false | The AWS profile path, if profile details not stored in default path. | | generatePresSignedUrl | bool | false | A boolean flag to control if either a pre-signed url is returned after successful upload or only the name of bucket and the artifacts S3 object key. | | preSignedUrlExpiryInMinutes | int | true | The number of minutes the generated pre-signed url should be accessible. | | copyBufferSize | int | false | The buffer size to use when copying data from the original artifact to the blob stream. There is a minimum size of 5 MB which is set when the given value is lower.| @@ -874,8 +876,8 @@ The Queue Message's payload will be the blob name (`/` "monitorS3Blob": { "endpoint": "http://localhost:9000", "bucketName": "myS3Bucket", - "accountKeyName": "minioUser", - "secretsFile": "C:\\Temp\\s3secret", + "accessKeyId": "minioUser", + "secretAccessKeyFile": "C:\\Temp\\s3secret", "regionName": "us-east-1", "generatePresSignedUrl" : true, "preSignedUrlExpiryInMinutes" : 15, @@ -897,8 +899,8 @@ The Queue Message's payload will be the blob name (`/` "monitorS3Blob": { "endpoint": "http://localhost:9000", "bucketName": "myS3Bucket", - "accountKeyName": "minioUser", - "accountKey": "mySecretPassword", + "accessKeyId": "minioUser", + "secretAccessKey": "mySecretPassword", "regionName": "us-east-1", "generatePresSignedUrl" : true, "preSignedUrlExpiryInMinutes" : 15, diff --git a/src/Microsoft.Diagnostics.Monitoring.Options/OptionsDisplayStrings.Designer.cs b/src/Microsoft.Diagnostics.Monitoring.Options/OptionsDisplayStrings.Designer.cs index e74f1ec727a..30e7adfd8c3 100644 --- a/src/Microsoft.Diagnostics.Monitoring.Options/OptionsDisplayStrings.Designer.cs +++ b/src/Microsoft.Diagnostics.Monitoring.Options/OptionsDisplayStrings.Designer.cs @@ -1284,20 +1284,29 @@ public static string DisplayAttributeDescription_ProcessFilterType_Exact { } /// - /// Looks up a localized string similar to The path to secrets file containing the password to login on S3. + /// Looks up a localized string similar to The AWS AccessKeyId for IAM user to login. /// - public static string DisplayAttributeDescription_S3StorageEgressProviderOptions_AccountKey { + public static string DisplayAttributeDescription_S3StorageEgressProviderOptions_AccessKeyId { get { - return ResourceManager.GetString("DisplayAttributeDescription_S3StorageEgressProviderOptions_AccountKey", resourceCulture); + return ResourceManager.GetString("DisplayAttributeDescription_S3StorageEgressProviderOptions_AccessKeyId", resourceCulture); } } /// - /// Looks up a localized string similar to The user-name used to login. + /// Looks up a localized string similar to The AWS profile name to be used for login. /// - public static string DisplayAttributeDescription_S3StorageEgressProviderOptions_AccountKeyName { + public static string DisplayAttributeDescription_S3StorageEgressProviderOptions_AWSProfileName { get { - return ResourceManager.GetString("DisplayAttributeDescription_S3StorageEgressProviderOptions_AccountKeyName", resourceCulture); + return ResourceManager.GetString("DisplayAttributeDescription_S3StorageEgressProviderOptions_AWSProfileName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The AWS profile path, if profile details not stored in default path. + /// + public static string DisplayAttributeDescription_S3StorageEgressProviderOptions_AWSProfilePath { + get { + return ResourceManager.GetString("DisplayAttributeDescription_S3StorageEgressProviderOptions_AWSProfilePath", resourceCulture); } } @@ -1347,12 +1356,21 @@ public static string DisplayAttributeDescription_S3StorageEgressProviderOptions_ } } + /// + /// Looks up a localized string similar to The AWS SecretAccessKey associated AccessKeyId for IAM user to login. + /// + public static string DisplayAttributeDescription_S3StorageEgressProviderOptions_SecretAccessKey { + get { + return ResourceManager.GetString("DisplayAttributeDescription_S3StorageEgressProviderOptions_SecretAccessKey", resourceCulture); + } + } + /// /// Looks up a localized string similar to The path to secrets file to get the value for accountKey to connect to S3 storage. /// - public static string DisplayAttributeDescription_S3StorageEgressProviderOptions_SecretsFile { + public static string DisplayAttributeDescription_S3StorageEgressProviderOptions_SecretsAccessKeyFile { get { - return ResourceManager.GetString("DisplayAttributeDescription_S3StorageEgressProviderOptions_SecretsFile", resourceCulture); + return ResourceManager.GetString("DisplayAttributeDescription_S3StorageEgressProviderOptions_SecretsAccessKeyFile", resourceCulture); } } diff --git a/src/Microsoft.Diagnostics.Monitoring.Options/OptionsDisplayStrings.resx b/src/Microsoft.Diagnostics.Monitoring.Options/OptionsDisplayStrings.resx index 4dbde8272b2..e507150eefb 100644 --- a/src/Microsoft.Diagnostics.Monitoring.Options/OptionsDisplayStrings.resx +++ b/src/Microsoft.Diagnostics.Monitoring.Options/OptionsDisplayStrings.resx @@ -720,21 +720,29 @@ The endpoint of S3 to connect to. This is optional in case of using AWS storage. The description provided for the Endpoint parameter on S3StorageEgressProviderOptions. - - The path to secrets file containing the password to login on S3 - The description provided for the AccountKey parameter on S3StorageEgressProviderOptions. + + The AWS profile name to be used for login + The description provided for the AWSProfileName parameter on S3StorageEgressProviderOptions. + + + The AWS profile path, if profile details not stored in default path + The description provided for the AWSProfilePath parameter on S3StorageEgressProviderOptions. The name of the S3 region The description provided for the RegionName parameter on S3StorageEgressProviderOptions. - + The path to secrets file to get the value for accountKey to connect to S3 storage - The description provided for the SecretsFile parameter on S3StorageEgressProviderOptions. + The description provided for the SecretsAccessKeyFile parameter on S3StorageEgressProviderOptions. + + + The AWS AccessKeyId for IAM user to login + The description provided for the AccessKeyId parameter on S3StorageEgressProviderOptions. - - The user-name used to login - The description provided for the AccountKeyName parameter on S3StorageEgressProviderOptions. + + The AWS SecretAccessKey associated AccessKeyId for IAM user to login + The description provided for the SecretAccessKey parameter on S3StorageEgressProviderOptions. A boolean flag indicates if the return value of egress provider should be a pre-signed URL or only the bucket name and object id of uploaded entry. diff --git a/src/Microsoft.Diagnostics.Monitoring.Options/S3StorageEgressProviderOptions.cs b/src/Microsoft.Diagnostics.Monitoring.Options/S3StorageEgressProviderOptions.cs index 65fb402b20f..8e580a231ed 100644 --- a/src/Microsoft.Diagnostics.Monitoring.Options/S3StorageEgressProviderOptions.cs +++ b/src/Microsoft.Diagnostics.Monitoring.Options/S3StorageEgressProviderOptions.cs @@ -31,18 +31,27 @@ internal sealed class S3StorageEgressProviderOptions : [Display( ResourceType = typeof(OptionsDisplayStrings), - Description = nameof(OptionsDisplayStrings.DisplayAttributeDescription_S3StorageEgressProviderOptions_AccountKeyName))] - public string AccountKeyName { get; set; } + Description = nameof(OptionsDisplayStrings.DisplayAttributeDescription_S3StorageEgressProviderOptions_AccessKeyId))] + public string AccessKeyId { get; set; } [Display( ResourceType = typeof(OptionsDisplayStrings), - Description = nameof(OptionsDisplayStrings.DisplayAttributeDescription_S3StorageEgressProviderOptions_SecretsFile))] - public string SecretsFile { get; set; } + Description = nameof(OptionsDisplayStrings.DisplayAttributeDescription_S3StorageEgressProviderOptions_SecretsAccessKeyFile))] + public string SecretsAccessKeyFile { get; set; } [Display( ResourceType = typeof(OptionsDisplayStrings), - Description = nameof(OptionsDisplayStrings.DisplayAttributeDescription_S3StorageEgressProviderOptions_AccountKey))] - public string AccountKey { get; set; } + Description = nameof(OptionsDisplayStrings.DisplayAttributeDescription_S3StorageEgressProviderOptions_SecretAccessKey))] + public string SecretAccessKey { get; set; } + + [Display( + ResourceType = typeof(OptionsDisplayStrings), + Description = nameof(OptionsDisplayStrings.DisplayAttributeDescription_S3StorageEgressProviderOptions_AWSProfileName))] + public string AwsProfileName { get; set; } + [Display( + ResourceType = typeof(OptionsDisplayStrings), + Description = nameof(OptionsDisplayStrings.DisplayAttributeDescription_S3StorageEgressProviderOptions_AWSProfilePath))] + public string AwsProfileFilePath { get; set; } [Display( ResourceType = typeof(OptionsDisplayStrings), @@ -62,7 +71,7 @@ internal sealed class S3StorageEgressProviderOptions : public IEnumerable Validate(ValidationContext validationContext) { - if (string.IsNullOrEmpty(SecretsFile) && string.IsNullOrEmpty(AccountKey)) + if (string.IsNullOrEmpty(SecretsAccessKeyFile) && string.IsNullOrEmpty(SecretAccessKey)) yield return new ValidationResult(OptionsDisplayStrings.ErrorMessage_EgressS3FailedMissingSecrets); if (string.IsNullOrEmpty(BucketName)) diff --git a/src/Tools/dotnet-monitor/Egress/S3/S3StorageEgressProvider.cs b/src/Tools/dotnet-monitor/Egress/S3/S3StorageEgressProvider.cs index 7a2d96e70bc..f0d3e4a3075 100644 --- a/src/Tools/dotnet-monitor/Egress/S3/S3StorageEgressProvider.cs +++ b/src/Tools/dotnet-monitor/Egress/S3/S3StorageEgressProvider.cs @@ -8,6 +8,7 @@ using System.Threading; using System.Threading.Tasks; using Amazon.Runtime; +using Amazon.Runtime.CredentialManagement; using Amazon.S3; using Amazon.S3.Model; using Amazon.S3.Transfer; @@ -130,23 +131,41 @@ private string GetResourceId(IAmazonS3 client, S3StorageEgressProviderOptions op private static async Task CreateClientAsync(S3StorageEgressProviderOptions options, CancellationToken cancellationToken) { - string accountKey; - if (!string.IsNullOrEmpty(options.SecretsFile) && File.Exists(options.SecretsFile)) - accountKey = await WrapException(async () => (await File.ReadAllTextAsync(options.SecretsFile, cancellationToken)).Trim()); - else - accountKey = options.AccountKey; - - var credentials = new BasicAWSCredentials(options.AccountKeyName, accountKey); - var config = new AmazonS3Config - { - ServiceURL = options.Endpoint, - AuthenticationRegion = options.RegionName, - ForcePathStyle = true - }; - var client = new AmazonS3Client(credentials, config); + AWSCredentials awsCredentials = null; + AmazonS3Config configuration = new(); + // use the specified access key and the secrets taken from configuration or a local file + if (!string.IsNullOrEmpty(options.AccessKeyId) && (!string.IsNullOrEmpty(options.SecretAccessKey) || !string.IsNullOrEmpty(options.SecretsAccessKeyFile))) + { + string secretAccessKeyId; + if (!string.IsNullOrEmpty(options.SecretsAccessKeyFile) && File.Exists(options.SecretsAccessKeyFile)) + secretAccessKeyId = await WrapException(async () => (await File.ReadAllTextAsync(options.SecretsAccessKeyFile, cancellationToken)).Trim()); + else + secretAccessKeyId = options.SecretAccessKey; + awsCredentials = new BasicAWSCredentials(options.AccessKeyId, secretAccessKeyId); + + configuration.ForcePathStyle = true; + configuration.ServiceURL = options.Endpoint; + configuration.AuthenticationRegion = options.RegionName; + } + // use configured AWS profile + else if (!string.IsNullOrEmpty(options.AwsProfileName)) + { + CredentialProfileStoreChain chain = !string.IsNullOrEmpty(options.AwsProfileFilePath) + ? new CredentialProfileStoreChain(options.AwsProfileFilePath) + : new CredentialProfileStoreChain(); + + if (!chain.TryGetAWSCredentials(options.AwsProfileName, out awsCredentials)) + throw new AmazonClientException("AWS profile not found"); + } + + awsCredentials ??= FallbackCredentialsFactory.GetCredentials(); + + if (awsCredentials == null) + throw new AmazonClientException("Failed to find AWS Credentials for constructing AWS service client"); - await VerifyBucketExistsAsync(client, options.BucketName, cancellationToken); - return client; + AmazonS3Client s3Client = new(awsCredentials, configuration); + await VerifyBucketExistsAsync(s3Client, options.BucketName, cancellationToken); + return s3Client; } private static async Task InitMultiPartUploadAsync(IAmazonS3 client, string bucketName, EgressArtifactSettings artifactSettings, CancellationToken cancellationToken)