What I want to achieve
In my past experiments the AWS credentials were 'magically' set in the background. To learn more about AWS credentials I will remove step by step the 'magic' and set credentials explicit in my code.
Cleanup
In my first experiment I set up the credentials on my Windows machine.
To ensure, that they are provided I test with my SNS-Test Program from my last post:
package aws;
import software.amazon.awssdk.services.sns.SnsClient;
import software.amazon.awssdk.services.sns.model.ListTopicsRequest;
import software.amazon.awssdk.services.sns.model.ListTopicsResponse;
public class CredentialsTest {
public static void main(String[] args) {
SnsClient snsClient = SnsClient.builder().build();
ListTopicsRequest request = ListTopicsRequest.builder().build();
ListTopicsResponse result = snsClient.listTopics(request);
System.out.println("Status was " + result.sdkHttpResponse().statusCode() + "\n\nTopics\n\n" + result.topics());
}
}
Result: A list of my SNS topics
To remove the 'magic' I rename the files credentials and config in C:\Users\USERNAME
\.aws folder to credentials_backup and config_backup.
Start CredentialsTest and the result: A list of my SNS topics.
So the credentials are provided by another mechanism.
Next try to remove the 'magic' I remove environment variables AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY and AWS_DEFAULT_REGION.
As I have started my IDE with this environment variables set, I need to restart IDE first.
Start CredentialsTest and the result:
Exception in thread "main" software.amazon.awssdk.core.exception.SdkClientException: Unable to load region from any of the providers in the chain software.amazon.awssdk.regions.providers.DefaultAwsRegionProviderChain@4372b9b6: [software.amazon.awssdk.regions.providers.SystemSettingsRegionProvider@3e6f3f28: Unable to load region from system settings. Region must be specified either via environment variable (AWS_REGION) or system property (aws.region)., software.amazon.awssdk.regions.providers.AwsProfileRegionProvider@4816278d: No region provided in profile: default, software.amazon.awssdk.regions.providers.InstanceProfileRegionProvider@1ecee32c: Unable to contact EC2 metadata service.]
at software.amazon.awssdk.core.exception.SdkClientException$BuilderImpl.build(SdkClientException.java:98)
Provide region:
SnsClient snsClient = SnsClient.builder().region(Region.EU_CENTRAL_1).build();
Result:
Exception in thread "main" software.amazon.awssdk.core.exception.SdkClientException: Unable to load credentials from any of the providers in the chain AwsCredentialsProviderChain(credentialsProviders=[SystemPropertyCredentialsProvider(), EnvironmentVariableCredentialsProvider(), WebIdentityTokenCredentialsProvider(), ProfileCredentialsProvider(), ContainerCredentialsProvider(), InstanceProfileCredentialsProvider()]) : [SystemPropertyCredentialsProvider(): Unable to load credentials from system settings. Access key must be specified either via environment variable (AWS_ACCESS_KEY_ID) or system property (aws.accessKeyId)., EnvironmentVariableCredentialsProvider(): Unable to load credentials from system settings. Access key must be specified either via environment variable (AWS_ACCESS_KEY_ID) or system property (aws.accessKeyId)., WebIdentityTokenCredentialsProvider(): Either the environment variable AWS_WEB_IDENTITY_TOKEN_FILE or the javaproperty aws.webIdentityTokenFile must be set., ProfileCredentialsProvider(): Profile file contained no credentials for profile 'default': ProfileFile(profiles=[]), ContainerCredentialsProvider(): Cannot fetch credentials from container - neither AWS_CONTAINER_CREDENTIALS_FULL_URI or AWS_CONTAINER_CREDENTIALS_RELATIVE_URI environment variables are set., InstanceProfileCredentialsProvider(): Unable to load credentials from service endpoint.]
at software.amazon.awssdk.core.exception.SdkClientException$BuilderImpl.build(SdkClientException.java:98)
OK, looks good so far.
Remove region from code and restore files credentials and config in C:\Users\USERNAME
\.aws folder.
Run CredentialsTest, Result: A list of my SNS topics.
Rename the files credentials and config in C:\Users\USERNAME
\.aws folder to credentials_backup and config_backup again.
Run CredentialsTest, Result: Unable to load region error again.
The 'magic' has been removed,
ProfileCredentialsProvider
Restore files credentials and config in C:\Users\USERNAME
\.aws folder.
Empty [default] block and create a new [CredentialsTest] block:
[default]
[CredentialsTest]
aws_access_key_id = My_AWS_Access_Key_Id
aws_secret_access_key = My_AWS_Secret_Access_Key
[default]
[CredentialsTest]
region = eu-central-1
Run CredentialsTest, Result:
2020-09-09 21:13:17 [main] WARN software.amazon.awssdk.profiles.internal.ProfileFileReader:105 - Ignoring profile 'CredentialsTest' on line 3 because it did not start with 'profile ' and it was not 'default'.
Exception in thread "main" software.amazon.awssdk.core.exception.SdkClientException: Unable to load region from any of the providers in the chain software.amazon.awssdk.regions.providers.DefaultAwsRegionProviderChain@260e86a1: [software.amazon.awssdk.regions.providers.SystemSettingsRegionProvider@59e505b2: Unable to load region from system settings. Region must be specified either via environment variable (AWS_REGION) or system property (aws.region)., software.amazon.awssdk.regions.providers.AwsProfileRegionProvider@8e50104: No region provided in profile: default, software.amazon.awssdk.regions.providers.InstanceProfileRegionProvider@43b6123e: Unable to contact EC2 metadata service.]
So I used to try to work with the ProfileCredentialsProvider this way:
import com.amazonaws.auth.profile.ProfileCredentialsProvider;
SnsClient snsClient = SnsClient.builder().credentialsProvider(new ProfileCredentialsProvider("CredentialsTest")).build();
Unfortunatly this won't compile because:
The method credentialsProvider(AwsCredentialsProvider) in the type AwsClientBuilder is not applicable for the arguments (ProfileCredentialsProvider)
Refactor to:
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
import software.amazon.awssdk.auth.credentials.ProfileCredentialsProvider;
AwsCredentialsProvider credentialsProvider = ProfileCredentialsProvider.builder().profileName("CredentialsTest").build();
SnsClient snsClient = SnsClient.builder().credentialsProvider(credentialsProvider).build();
Result:
2020-09-09 21:22:40 [main] WARN software.amazon.awssdk.profiles.internal.ProfileFileReader:105 - Ignoring profile 'CredentialsTest' on line 3 because it did not start with 'profile ' and it was not 'default'.
Exception in thread "main" software.amazon.awssdk.core.exception.SdkClientException: Unable to load region from any of the providers in the chain software.amazon.awssdk.regions.providers.DefaultAwsRegionProviderChain@78f5c518: [software.amazon.awssdk.regions.providers.SystemSettingsRegionProvider@f107c50: Unable to load region from system settings. Region must be specified either via environment variable (AWS_REGION) or system property (aws.region)., software.amazon.awssdk.regions.providers.AwsProfileRegionProvider@4ebff610: No region provided in profile: default, software.amazon.awssdk.regions.providers.InstanceProfileRegionProvider@8692d67: Unable to contact EC2 metadata service.]
Hmkay, enhance the code with a region; need to set this explicit, could not find any way to read this from the config file.
SnsClient snsClient = SnsClient.builder().credentialsProvider(credentialsProvider).region(Region.EU_CENTRAL_1).build();
Result: A list of my SNS topics.
Rename the files credentials and config in C:\Users\USERNAME
\.aws folder to credentials_backup and config_backup again.
Run CredentialsTest, Result: Profile file contained no credentials for profile 'CredentialsTest' error.
Remove ProfileCredentialsProvider and Region from code.
Run CredentialsTest, Result: Unable to load region error again.
Own AwsCredentialsProvider implementation
Write an own credential provider, the simplest way:
package aws;
import software.amazon.awssdk.auth.credentials.AwsCredentials;
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
public class IngosCredentialProvider implements AwsCredentialsProvider {
public AwsCredentials resolveCredentials() {
System.out.println("IngosCredentialProvider::resolveCredentials called");
AwsCredentials credentials = new AwsCredentials() {
public String secretAccessKey() {
return "My_AWS_Secret_Access_Key";
}
public String accessKeyId() {
return "My_AWS_Access_Key_Id";
}
};
return credentials;
}
}
Use your own credentials provider in code, don't forget the region:
package aws;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.sns.SnsClient;
import software.amazon.awssdk.services.sns.model.ListTopicsRequest;
import software.amazon.awssdk.services.sns.model.ListTopicsResponse;
public class CredentialsTest {
public static void main(String[] args) {
SnsClient snsClient = SnsClient.builder().credentialsProvider(new IngosCredentialProvider()).region(Region.EU_CENTRAL_1).build();
ListTopicsRequest request = ListTopicsRequest.builder().build();
ListTopicsResponse result = snsClient.listTopics(request);
System.out.println("Status was " + result.sdkHttpResponse().statusCode() + "\n\nTopics\n\n" + result.topics());
}
}
Run CredentialsTest, Result: A list of my SNS topics.