Categories
AWS

Example React application on AWS Amplify

I followed the steps through this tutorial: https://aws.amazon.com/de/getting-started/hands-on/build-react-app-amplify-graphql/

I is mostly well documented. But I had some obstacles:

Build specification

I had to add the backend part to the build specification:

version: 1
backend:
  phases:
    build:
      commands:
        - '# Execute Amplify CLI with the helper script'
        - amplifyPush --simple
frontend:
  phases:
    preBuild:
      commands:
        - npm ci
    build:
      commands:
         - npm run build
  artifacts:
    baseDirectory: build
    files:
      - '**/*'
  cache:
    paths:
      - node_modules/**/*

Service role

I had to create and add a service role.

First create a service role in IAM console, named it “AmplifyConsoleServiceRole-AmplifyRole”:

Then add this role in the general settings of the Amplify application:

Amplify CLI to latest version

I had to set the Live package updates for the Amplify CLI to the latest version in build image settings:

To be continued…

Unfortunately this took too much time, so I have to do the last steps (4: API and database; 5: storage) another time. Maybe there will be some other challanges I can write down here.

Categories
AWS Java

Credentials

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<SnsClientBuilder,
 SnsClient> 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.

Categories
AWS Java

Simple Notification Service

What I want to do today

Create a SNS, send and receive messages.

Create SNS

Just go to Amazon SNS -> Topics -> Create topic and set a name for the topic:

In the next screen I create a subscription with Protocol Email and my email address. Immediately I got an email with a link to subscribe to the topic. After Confirmation I can check in the Subsriptions view that the status has changed to “Confirmed”.

There is a Amazon SNS -> Topics -> MyFirstTestTopic -> Publish message function in the AWS Console to publish a message to topic, which is a good way to test the service.

Java Code

I continue with my test project from my last post.

List SNS Topics

SnsClient snsClient = SnsClient.builder().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());

Lists all topics of my AWS account.

Publish message to topic

SnsClient snsClient = SnsClient.builder().region(Region.EU_CENTRAL_1).build();
String topicArn = "arn:aws:sns:eu-central-1:175335015168:MyFirstTestTopic";
String message = "This is a test (c)DerIngo";
PublishRequest request = PublishRequest.builder().message(message).topicArn(topicArn).build();
PublishResponse result = snsClient.publish(request);
System.out.println(result.messageId() + " Message sent. Status was " + result.sdkHttpResponse().statusCode());

Test email received, it works, YAY!

I