Follow the step-by-step instructions below to build a serverless backend. Click on each step number to expand the section.

  • Step 1. Create an IAM policy for a custom IAM role

    To grant Lambda permissions to handle backend requests for your website, you create a custom IAM policy that grants permissions for AWS services to perform the following actions:

    • AppStream 2.0 creates a streaming URL. Customers are redirected to this streaming URL when they sign in to the Example Corp. website.
    • Amazon SES sends your customers an email when they sign up for the Example Corp. online software trial.
    • Amazon CloudWatch logging is enabled.

    Complete the following steps to create the custom IAM policy.
    1. Open the IAM console at https://console.aws.amazon.com/iam/.
    2. In the navigation pane, choose Policies.
    3. If this is your first time choosing Policies, the Welcome to Managed Policies page appears. Choose Get Started.
    4. Choose Create policy.
    5. Choose the JSON tab.
    6. Copy and paste the following JSON policy into the policy document field.
    {
        "Version": "2012-10-17",
        "Statement": [{
                "Effect": "Allow",
                "Action": "appstream:CreateStreamingURL",
                "Resource": [
                    "arn:aws:appstream:REGION-CODE:ACCOUNT-ID-WITHOUT-HYPHENS:fleet/FLEET-NAME",
                    "arn:aws:appstream:REGION-CODE:ACCOUNT-ID-WITHOUT-HYPHENS:stack/STACK-NAME"
                ]
            },
            {
                "Effect": "Allow",
                "Action": "ses:SendEmail",
                "Resource": "*"
            },
            {
                "Effect": "Allow",
                "Action": [
                    "logs:CreateLogGroup",
                    "logs:CreateLogStream",
                    "logs:PutLogEvents"
                ],
                "Resource": "*"
            }
        ]
    }
    

    7. Choose the value for REGION-CODE that corresponds to the AWS Region where your AppStream 2.0 stack and fleet exists. The ACCOUNT-ID-WITHOUT-HYPHENS should be replaced with your AWS Account ID. Replace STACK-NAME and FLEET-NAME with the names of the stack and fleet.
    8. When you’re done, choose Review policy.
    9. For Name, type the following name for your new policy: examplecorp_lambda_tin_policy.
    10. Choose Create policy.

  • Step 2. Create an IAM service role that allows Lambda functions to call AWS services

    Lambda requires an IAM service role to allow the service to access resources in other services on your behalf. Complete the following steps to create an IAM service role and attach the policy that you created to this role.

    1. Open the IAM console at https://console.aws.amazon.com/iam/.
    2. In the navigation pane, under Roles, choose Create role.
    3. For Select type of trusted entity, keep AWS service selected.
    4. Choose Lambda, and then choose Next: Permissions.
    5. In the Filter policies search box, type examplecorp_lambda_tin_policy. When the policy appears in the list, select the check box next to the policy name.
    6. Choose Next: Tags. Although you can specify a tag for the policy, a tag is not required.
    7. Choose Next: Review.
    8. For Role name, type examplecorp_lambda_tin_role.
    9. Choose Create role.

  • Step 3. Create and configure a Lambda function

    Complete the following steps to create a Lambda function.

    1. Open the Lambda console at https://console.aws.amazon.com/lambda/.
    2. Do one of the following:
        • If you haven’t created any Lambda functions, a Getting Started page displays. Under Getting Started, choose Create a function.
        • If you have created a Lambda function, in the upper right corner of the Functions page, choose Create function.
    3. On the Create function page, keep Author from scratch selected.
    4. Under Basic information, do the following:
        • For Name, type examplecorp_lambda_tin_function.
        • For Runtime, choose Node.js 8.10.
    5. Under Permissions, choose the icon next to Choose or create an execution role. Then do the following:
        • For Execution role, choose Use an existing role.
        • For Existing role, choose examplecorp_lambda_tin_role from the list.
    6. Choose Create function.
    7. In the Function code section, on the index.js tab, the placeholder code displays. Delete the placeholder code, and copy and paste the following code onto the tab:

    // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
    // SPDX-License-Identifier: Apache-2.0
    
    const AWS = require('aws-sdk');
    const appstream = new AWS.AppStream;
    const ses = new AWS.SES();
    const crypto = require('crypto');
    
    exports.handler = (event, context, callback) => {
        var eventdata = JSON.parse(event.body);
    
        var length = 16; //Adjust this value to shorten or lengthen the AS2 username - 32 chars is max length an AS2 username can be
        var username = crypto.randomBytes(Math.ceil(length / 2)).toString('hex').slice(0, length); //generates a random string
        
        /* 
        Username could also be the name or email parameter from the event body
    
        var username = eventdata.name
        var username = eventdata.email
    
        */
        
        console.log("username: " + username);
    
        var params = { 
            FleetName: '<Fleet-Name>', /* required */
            StackName: '<Stack-Name>', /* required */
            UserId: username,
            Validity: 5 //TTL of URL
    
        };
    
        createas2streamingurl(params, eventdata, context.awsRequestId, callback);
    
    };
    
    function errorResponse(errorMessage, awsRequestId, callback) { //Function for handling error messaging back to client
        callback(null, {
            statusCode: 500,
            body: JSON.stringify({
                Error: errorMessage,
                Reference: awsRequestId,
            }),
            headers: {
                'Access-Control-Allow-Origin': '<origin-domain>', //This should be the domain of the website that originated the request, example: amazonaws.com
            },
        });
    }
    
    function createas2streamingurl(params, sesdata, awsRequestId, callback) {
        var request = appstream.createStreamingURL(params);
        request.
            on('success', function (response) { //Successful Response
                console.log("Success! AS2 Streaming URL created.");
                var output = response.data;
                var url = output.StreamingURL;
                sendEmail(sesdata); //With a successful AS2 URL generated, trigger the SES SendEmail function
                callback(null, {
                    statusCode: 201,
                    body: JSON.stringify({
                        Message: url,
                        Reference: awsRequestId,
                    }),
                    headers: {
                        'Access-Control-Allow-Origin': '<origin-domain>', //This should be the domain of the website that originated the request, example: amazonaws.com
                    },
                });
            }).
            on('error', function (response) { //an error occoured 
                console.log("error: "  + JSON.stringify(response.message));
                errorResponse('Error creating AS2 streaming URL.', awsRequestId, callback);
    
            }).
            send();
    }
    
    function sendEmail(data) {
        var sender = "<sender@example.com>"; //Sender needs to be a verified address in SES
        var receiver = data.email.trim(); /*Trim the string of any preceding and trailing whitespaces*/
        var name = data.name.trim();
        var params = {
            Destination: {
                ToAddresses: [receiver]
            },
            Message: {
                Body: {
                    Text: {
                        Data: 'Hello ' + name + ',' + '\n\nThank you for trying Example Corp\'s Application Suite.',
                        Charset: 'UTF-8'
                    }
                },
                Subject: {
                    Data: 'Thank you for trying Example Corp ' + name,
                    Charset: 'UTF-8'
                }
            },
            Source: sender
        };
        console.log("Sending Email.");
        ses.sendEmail(params, function (err, data) {
            if (err) console.log(err, err.stack); // an error occurred
            else console.log(data);           // successful response
        });
    }
    

    8. Replace the following variables with your own values:

    • <Stack-Name>
    • <Fleet-Name>
    • <origin-domain>

    Where:
    • <Stack-Name> is the name of the stack from which you want to create the streaming URL.
    • <Fleet-Name> is the name of the fleet that is associated with the stack from which you want this function to generate streaming URLs.
    • <origin-domain> is the domain of the website originating the request to API Gateway, in this case the full S3 website URL (ex. http://bucket-name.s3-website.region.amazonaws.com), that you set up for this project.

    9. Save the function and close the Lambda console.