admin管理员组

文章数量:1287593

AWS-hosted Alexa skills offer 3 lambda functions, one S3 bucket and one DynamoDB database; but give you very little control over the databases and ultimately they are constrained in size.

I want to extend the S3 storage offered by Amazon for my Alexa Skill to additionally use my personal AWS S3 storage. Using this weblink I have managed to add my personal DynamoDB additionally to the single database offered by AWS hosting and it seems to work. I now have one DynamoDB database for users and the second for content. This involved setting up a Role in my personal AWS account, with a trust relationship to the ARN for my AWS-hosted lambda function, which allows it to assume the role with permissions to DynamoDB and S3. (I set both up at the same time and DynamoDB works.)

The problem I am encountering is with S3 pre-signed URLs, which I can’t make work. Pre-signed URLs use a required util file:

const AWS = require('aws-sdk');

const s3SigV4Client = new AWS.S3({
    signatureVersion: 'v4',
    region: process.env.S3_PERSISTENCE_REGION
});

module.exports.getS3PreSignedUrl = function getS3PreSignedUrl(s3ObjectKey) {

    const bucketName = process.env.S3_PERSISTENCE_BUCKET;
    const s3PreSignedUrl = s3SigV4Client.getSignedUrl('getObject', {
        Bucket: bucketName,
        Key: s3ObjectKey,
        Expires: 60*1 // the Expires is capped for 1 minute
    });
    console.log(`Util.s3PreSignedUrl: ${s3ObjectKey} URL ${s3PreSignedUrl}`);
    return s3PreSignedUrl;

}

As a minimum I need to change the bucket name (highlighted) from the S3_PERSISTENCE_BUCKET to the name of my new bucket in my personal account.; but I want to do this conditionally so that the bulk of my code can call the AWS-hosted S3 bucket, but occasionally I want to be access the additional content in the personally-hosted S3 bucket. As you can see from the code below, I’ve had a few attempts.

async handle(handlerInput) {
    const intentName = Alexa.getIntentName(handlerInput.requestEnvelope);
    
    // 1. Assume the AWS resource role using STS AssumeRole Action
    const STS = new AWS.STS({ apiVersion: '2011-06-15' });
    const credentials = await STS.assumeRole({
        RoleArn: 'arn:aws:iam::{{MY ROLE}}',
        RoleSessionName: 'AlexaHostedLambdaRole' 
    }, (err, res) => {
        if (err) {
            console.log('AssumeRole FAILED: ', err);
            throw new Error('Error while assuming role');
        }
        return res;
    }).promise();
    
    // 2. Make a new DynamoDB instance with the assumed role credentials
    //    and scan the DynamoDB table
    const dynamoDB = new AWS.DynamoDB({
        apiVersion: '2012-08-10',
        accessKeyId: credentials.Credentials.AccessKeyId,
        secretAccessKey: credentials.Credentials.SecretAccessKey,
        sessionToken: credentials.Credentials.SessionToken
    });

    var params = {
      TableName: "AlexaHostedTable",
      Key: {
        memory_id: { S: "00002" },
      }
    };

    const tableData = await dynamoDB.getItem(params, function (err, data) {
        if (err) {
            console.log('getItem FAILED', err);
            throw new Error('Error while getting Item from DynamoDB');
        } else {
        return data;}
    }).promise();

    // 3. Make a new S3 instance with the assumed role credentials

    const s3Two = new AWS.S3({
        apiVersion: '2012-08-10',
        accessKeyId: credentials.Credentials.AccessKeyId,
        secretAccessKey: credentials.Credentials.SecretAccessKey,
        sessionToken: credentials.Credentials.SessionToken
    });

    // ... Use table data as required ...
    var audioUrl =  Util.getS3PreSignedUrl({{..PREFIX..}}tableData.Item.file.S +".mp3").replace(/&/g,'&');            //Marker
 //        const Util2 = require('./util2.js'); // pre-signs URLs from personal S3 bucket
    var audioUrl2 = Util.getS3PreSignedUrl(tableData.Item.file.S +".mp3").replace(/&/g,'&');            //Marker
      
      speakOutput ='Introductory text blah blah blah'+`<audio src="${audioUrl}"/>`+`<audio src="${audioUrl2}"/>`+'Suffix text blah blah blah';                    // Marker
      console.log ("speakOutput: ", speakOutput);
         
    return handlerInput.responseBuilder
        .speak(speakOutput)
        .reprompt('add a reprompt if you want to keep the session open for the user to respond')
        .getResponse();
}
};

I have tried:

  • Replicating the util.js function, so that there are two versions for Alexa-hosted and personally-hosted bucket names
  • Calling the personally-hosted bucket name outside of the util.js function
  • Converting the util.js into a function so that it can accept parameters such as the bucket name

With the current version of the code, the references to the old bucket will work. Alexa appears to created pre-signed URLs to the bucket, but I think this is a red herring as they don't work.

本文标签: