How to set up Amazon DynamoDB Client on AWS (JAVA) - amazon-web-services

Trying to set up a client for my Amazon DynamoDB in Java 8 and am running into this error when I try to run my lambda function locally. I am trying to connect to Amazon DynamoDB and I already have set up in AWS Management Console.
Error trying to commit audit record:com.amazonaws.services.dynamodbv2.model.AmazonDynamoDBException: The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details. (Service: AmazonDynamoDBv2; Status Code: 400; Error Code: InvalidSignatureException;
I am still new to AWS and trying to understand how it works. I am sure the credentials I provided matched the ones I have.
AmazonDynamoDB client = AmazonDynamoDBClient.builder()
.withRegion("us-east-2")
.withCredentials(new AWSStaticCredentialsProvider(new
BasicAWSCredentials("key","private key")))
.build();
DynamoDB dynamoDB = new DynamoDB(client);
Table table = dynamoDB.getTable("tableName")

Maybe you can try out changing according to example in AWS docs, without explicitly setting credential provider.
https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/CodeSamples.Java.html

This is my code for creating a table and this is working:
BasicAWSCredentials awsCreds = new BasicAWSCredentials("access_key_id", "secret_key_id");
AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard()
.withRegion(Regions.US_EAST_1)
.withCredentials(new AWSStaticCredentialsProvider(awsCreds))
.build();
// AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard()
// .withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration("http://localhost:8000", "us-west-2"))
// .build();
DynamoDB dynamoDB = new DynamoDB(client);
String tableName = "Songs";
try {
System.out.println("Attempting to create table; please wait...");
Table table = dynamoDB.createTable(tableName,
Arrays.asList(
new KeySchemaElement("year", KeyType.HASH), // Partition
// key
new KeySchemaElement("title", KeyType.RANGE)), // Sort key
Arrays.asList(new AttributeDefinition("year", ScalarAttributeType.N),
new AttributeDefinition("title", ScalarAttributeType.S)),
new ProvisionedThroughput(10L, 10L));
table.waitForActive();
System.out.println("Success. Table status: " + table.getDescription().getTableStatus());
} catch (Exception e) {
System.err.println("Unable to create table: ");
System.err.println(e.getMessage());
}

Related

Getting HTTP 400: Bad Request when trying to connect to local DynamoDB with Kotlin

I'm using AWS Kotlin client libraries from Kotlin and Ktor. When trying to connect to a local DynamoDB instance running in Docker I get the following error message:
DEBUG httpTraceMiddleware - DynamoDB-GetItem-835467a5-aedc-40ab-8c28-b56b625ccbd4/Attempt-1: HttpResponse: 400: Bad Request
I'm using the library version aws.sdk.kotlin:dynamodb:0.18.0-beta and my DynamoDB client configuration is the following. I used the sample provided in their official guide as a baseline.
suspend fun getValueForKey(tableNameVal: String, keyName: String, keyVal: String) {
val keyToGet = mutableMapOf<String, AttributeValue>()
keyToGet[keyName] = AttributeValue.S(keyVal)
val request = GetItemRequest {
key = keyToGet
tableName = tableNameVal
}
val provider = StaticCredentialsProvider.Builder().apply {
accessKeyId = "fakeMyKeyId"
secretAccessKey = "fakeSecretAccessKey"
}.build()
DynamoDbClient {
credentialsProvider = provider
region = "us-east-1"
endpointUrl = Url.parse("http://localhost:8000")
}.use { ddb ->
val returnedItem = ddb.getItem(request)
val numbersMap = returnedItem.item
numbersMap?.forEach { entry ->
println(entry.key)
println(entry.value)
}
}
}
I even tried adding fake access/secret keys based on the official guide, but the result is the same. So even if I don't specify the credentialsProvider parameter I get the same error.
From the command line, I'm able to perform a successful operation using the command below:
aws dynamodb get-item \
--table-name key_values \
--key '{"key": {"S": "key1"}}' \
--endpoint-url http://localhost:8000
I figured out there are mainly two reasons for such exceptions:
Using the wrong region, access key, and secret key for table creation & read/write
Not having a proper endpoint configured
Proper endpoint configuration
I had to create a "fake" EndpointProvider that returns a static local endpoint. Using the endpointUrl property of DynamoDbClient did not work for me. Skipping credentialsProvider also leads to a CredentialsProviderException.
Getting the Region, Access Key & Secret Key Right
I had to specify the region, access & secret key pairs on every operation. Missing any of that might lead to a scenario where incorrect configuration is read from .aws/credentials file or from environment variables mixing up the read/write or table creation operations. It seems, that the local DynamoDB returns an "HTTP 400 Bad Request" if access/secret key pairs or the target region are not the same when the table is created.
Example Command For Table Creation
#!/bin/bash
export AWS_SECRET_ACCESS_KEY=DUMMYIDEXAMPLE
export AWS_ACCESS_KEY_ID=DUMMYIDEXAMPLE
export AWS_DEFAULT_REGION=us-west-2
aws dynamodb create-table \
--table-name key_values \
--attribute-definitions AttributeName=key,AttributeType=S \
--key-schema AttributeName=key,KeyType=HASH \
--provisioned-throughput ReadCapacityUnits=5,WriteCapacityUnits=5 \
--endpoint-url http://localhost:8000
Example Command For Reading Table in Kotlin
The accessKeyId, secretAccessKey, and region attributes has to be the same as above.
val credentials = StaticCredentialsProvider.Builder().apply {
accessKeyId = "DUMMYIDEXAMPLE"
secretAccessKey = "DUMMYIDEXAMPLE"
}.build()
val endpoint = EndpointProvider { Endpoint(Url.parse("http://localhost:8000")) }
DynamoDbClient {
credentialsProvider = credentials
region = "us-west-2"
endpointProvider = endpoint
}.use { ddb ->
val returnedItem = ddb.getItem(request)
val numbersMap = returnedItem.item
numbersMap?.forEach { entry ->
println(entry.key)
println(entry.value)
}
}

How to invoke secrets from AWS Secrets Manager into code to get the data from an Amazon DyanmoDB table

I have stored AWS IAM user Access key's and Secret keys in a secret of AWS Secrets Manager.
This secret is helpful to get the data from an Amazon DynamoDB table, and keys's having full access to the Amazon DynamoDB table. I need to use this secret in java/.Net code to retrieve the data from DynamoDB table.
Secretname: dynamodbtesting
Below is the sample key names which I used while creating secret.
{
"aws_access_key_id": "value",
"aws_secret_access_key": "secret value"
}
How to use secret in java/.Net code to get the date from DynamoDB table?
Note: I could see one sample code after creation of secret in secret manager, is it helpful?
When using the AWS Java SDK, when you build the client which accesses dynamodb, you can pass credentials explicitly:
https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/credentials.html#credentials-explicit
For example:
BasicAWSCredentials awsCreds = new BasicAWSCredentials("access_key_id", "secret_key_id");
AmazonS3 dynamodbClient = AmazonDynamoDBClientBuilder.standard()
.withCredentials(new AWSStaticCredentialsProvider(awsCreds))
.build();
To answer your question: "How to use secret in java"
You can use the Secrets Manager Java API V2 to retrive a secret. The following Java code shows you how to perform this use case:
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient;
import software.amazon.awssdk.services.secretsmanager.model.GetSecretValueRequest;
import software.amazon.awssdk.services.secretsmanager.model.GetSecretValueResponse;
import software.amazon.awssdk.services.secretsmanager.model.SecretsManagerException;
//snippet-end:[secretsmanager.java2.get_secret.import]
/**
* To run this AWS code example, ensure that you have setup your development environment, including your AWS credentials.
*
* For information, see this documentation topic:
*
*https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/get-started.html
*/
public class GetSecretValue {
public static void main(String[] args) {
final String USAGE = "\n" +
"Usage:\n" +
" GetSecretValue <secretName> \n\n" +
"Where:\n" +
" secretName - the name of the secret (for example, tutorials/MyFirstSecret). \n";
if (args.length != 1) {
System.out.println(USAGE);
System.exit(1);
}
String secretName = args[0];
Region region = Region.US_EAST_1;
SecretsManagerClient secretsClient = SecretsManagerClient.builder()
.region(region)
.build();
getValue(secretsClient, secretName);
secretsClient.close();
}
//snippet-start:[secretsmanager.java2.get_secret.main]
public static void getValue(SecretsManagerClient secretsClient,String secretName) {
try {
GetSecretValueRequest valueRequest = GetSecretValueRequest.builder()
.secretId(secretName)
.build();
GetSecretValueResponse valueResponse = secretsClient.getSecretValue(valueRequest);
String secret = valueResponse.secretString();
System.out.println(secret);
} catch (SecretsManagerException e) {
System.err.println(e.awsErrorDetails().errorMessage());
System.exit(1);
}
}
//snippet-end:[secretsmanager.java2.get_secret.main]
}
You can find this example and others for this AWS Service here:
https://github.com/awsdocs/aws-doc-sdk-examples/tree/master/javav2/example_code/secretsmanager

How to use DefaultAWSCredentialsProviderChain() in java code to fetch credentials from instance profile and allow access to s3 bucket

I am working on a requirement where i want to connect to s3 bucket using springboot application.
When i am connecting through my local environment i am using seeting loadCredentials(true) which uses Amazon STS which fetches the temperoriy credentials using a role and allow access to s3 bucket.
When i am deploying to the qa/prd envirment i am setting loadCredentials(false) which uses DefaultAWSCredentialsProviderChain() class to fetch the credential from aws instance profile(role is assigned to ec2 instance) and allow access to s3 bucket. My code is
#Configuration
public class AmazonS3Config
{
static String clientRegion = "ap-south-1";
static String roleARN = "arn:aws:iam::*************:role/awss3acess";
static String roleSessionName = "bucket_storage_audit";
String bucketName = "testbucket";
//Depending on environment is set to true(for local environment) or false(for qa and prd environment)
private static AWSCredentialsProvider loadCredentials(boolean isLocal) {
final AWSCredentialsProvider credentialsProvider;
if (isLocal) {
AWSSecurityTokenService stsClient = AWSSecurityTokenServiceAsyncClientBuilder.standard()
.withCredentials(new ProfileCredentialsProvider())
.withRegion(clientRegion)
.build();
AssumeRoleRequest assumeRoleRequest = new AssumeRoleRequest().withDurationSeconds(3600)
.withRoleArn(roleARN)
.withRoleSessionName(roleSessionName);
AssumeRoleResult assumeRoleResult = stsClient.assumeRole(assumeRoleRequest);
Credentials creds = assumeRoleResult.getCredentials();
credentialsProvider = new AWSStaticCredentialsProvider(
new BasicSessionCredentials(creds.getAccessKeyId(),
creds.getSecretAccessKey(),
creds.getSessionToken())
);
} else {
System.out.println("inside default");
credentialsProvider = new DefaultAWSCredentialsProviderChain();
}
return credentialsProvider;
}
// Amazon s3client Bean return an instance of s3client
. #Bean
public AmazonS3 s3client() {
AmazonS3 s3Client = AmazonS3ClientBuilder.standard()
.withRegion(Regions.fromName(clientRegion))
.withCredentials(loadCredentials(false))
.build();
return s3Client;
}
}
My question since the credentials of instance profile rotate after every 12 hours my application will fail after 12 hours.
What will i do to avoid this from happening in my code.
You can directly use ProfileCredentialsProvider instead of DefaultAWSCredentialsProviderChain as there is no need in your case to chain the credsproviders.
and about your question, AWSCredentialsProvider has refresh() method that will reread the config file.when an Authentication exception Occurs while using S3Client you can retry again and call refresh() first.

DynamoDB Connection Biulding using AWS Java SDK by getting Keys from the user at runtime

I'm trying to connect to DynamoDB by getting AccessID and SecretKey from the user. AmazonDynamoDBClient has been depreciated and the replacement don't allow me to get credentials from the user and make a connection to DynamoDB. Here is my code snippet. The solution I'm getting is to keep the keys in a local file. I don't need this.
DynamoDB dynamoDB = null;
try {
System.out.println(1);
BasicAWSCredentials awsCreds = new BasicAWSCredentials(upDoc.getAccID(), upDoc.getAccKey());
System.out.println(2);
//AmazonDynamoDBClient is depreciated
AmazonDynamoDBClient client = new AmazonDynamoDBClient(awsCreds).withRegion(Regions.US_EAST_2);
System.out.println(3);
dynamoDB = new DynamoDB(client);
writer.append("Access Granted By AWS DynamoDB \n");
}catch(AmazonDynamoDBException e) {
writer.append("Access Denied By AWS DynamoDB \n");
writer.close();
return "Error occured. Kindly check logs to get the actual cause!";
}
Use AmazonDynamoDBClientBuilder
BasicAWSCredentials awsCreds = new BasicAWSCredentials(upDoc.getAccID(), upDoc.getAccKey());
AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().withRegion(Regions.US_EAST_2).withCredentials(awsCreds).build();

How to request "Snapshot Log" through AWS Java SDK?

Is it possible to request "Snapshot Logs" through AWS SDK somehow?
It's possible to do it through AWS console:
Cross posted to Amazon forum.
Requesting a log snapshot is a 3 step process. First you have to do an environment information request:
elasticBeanstalk.requestEnvironmentInfo(
new RequestEnvironmentInfoRequest()
.withEnvironmentName(envionmentName)
.withInfoType("tail"));
Then you have to retreive the environment information:
final List<EnvironmentInfoDescription> envInfos =
elasticBeanstalk.retrieveEnvironmentInfo(
new RetrieveEnvironmentInfoRequest()
.withEnvironmentName(environmentName)
.withInfoType("tail")).getEnvironmentInfo();
This returns a list of environment info descriptions, with the EC2 instance id and the URL to an S3 object that contains the log snapshot. You can then retreive the logs with:
DefaultHttpClient client = new DefaultHttpClient();
DefaultHttpRequestRetryHandler retryhandler =
new DefaultHttpRequestRetryHandler(3, true);
client.setHttpRequestRetryHandler(retryhandler);
for (EnvironmentInfoDescription environmentInfoDescription : envInfos) {
System.out.println(environmentInfoDescription.getEc2InstanceId());
HttpGet rq = new HttpGet(environmentInfoDescription.getMessage());
try {
HttpResponse response = client.execute(rq);
InputStream content = response.getEntity().getContent();
System.out.println(IOUtils.toString(content));
} catch ( Exception e ) {
System.out.println("Exception fetching " +
environmentInfoDescription.getMessage());
}
}
I hope this helps!