Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to obtain AWS IOT endpoint URL from within a CloudFormation template?

I want some of my Lambda resources to push to an AWS IOT endpoint using aws-sdk's AWS.IotData({ endpoint: url }) function - where endpoint is a required parameter.

Right now, I am passing the endpoint URL via an environment variable to my Lambda. However, when put into a SAM/CF template, I can't find a way to retrieve my IOT endpoint URL, so that I could simply !Ref it.

Browsing through the AWS resource type reference I did not find any resource that corresponds to an IOT endpoint.

It seems like IOT endpoint can only be provisioned manually, via AWS Console (enabled / disabled), as on the screenshot below:

IOT Endpoint AWS Console

Any advice on how to have control over provisioning an IOT endpoint or at least reading the IOT URL from within a SAM/CF template, without scripting this with aws-cli?

like image 765
jaccus Avatar asked Jun 16 '17 12:06

jaccus


People also ask

Where is my AWS IoT endpoint?

The AWS IoT device service endpoints support device-centric access to security and management services. To learn your account's device data endpoint, you can find it in the Settings page of your AWS IoT Core console.

How do I get IoT core endpoint?

It's not the account's personal endpoint, but the Thing's endpoint. Go to IoT Core -> Manage -> Things, select your thing -> Interact. Its the URL under the HTTPS part. It should be in the form xxxxxxxxxxxxxxxxx.iot.region.amazonaws.com, where the x's should contain mainly lowercase letters, and maybe some numbers.

How do you reference existing resources in CloudFormation?

To import existing resources into a CloudFormation stack, you need to provide: A template that describes the entire stack, including both the resources to import and (for existing stacks) the resources that are already part of the stack. Each resource to import must have a DeletionPolicy attribute in the template.

What is CloudFormation endpoint?

An endpoint enables you to create a private connection between your VPC and the service. The service may be provided by AWS, an AWS Marketplace Partner, or another AWS account.

What is AWS CloudFormation endpoint?

AWS CloudFormation Endpoints. To reduce data latency in your applications, most Amazon Web Services products allow you to select a regional endpoint to make your requests. An endpoint is a URL that is the entry point for a web service.

How do I find the AWS IoT Core-Data Plane endpoint?

To find the AWS IoT Core - data plane endpoint for your AWS account and AWS Region, use the describe-endpoint CLI command shown here, or the DescribeEndpoint REST API. This command returns your Data Plane API endpoint in the following format:

How is the region element set in AWS CloudFormation?

If the stack uses the default S3 bucket, the Region element is set by the pseudo parameter AWS::Region, which resolves to the Region where the AWS CloudFormation template is executed. For more on pseudo parameters, see Pseudo parameters reference.

Where can I find templates for AWS CloudFormation?

AWS Quick Start offers AWS CloudFormation templates and detailed deployment guides for popular IT workloads such as Microsoft Windows Server and SAP HANA. Sample solution templates show how to create an end-to-end solution with common applications.


2 Answers

For anyone interested in the solution with CloudFormation Custom Resource, I wrote a simple Lambda and a CF template that provides an IOT endpoint address to other CF stacks.

template.yaml

AWSTemplateFormatVersion: '2010-09-09'
Transform: 'AWS::Serverless-2016-10-31'
Resources:
  IotEndpointProvider:
    Type: 'AWS::Serverless::Function'
    Properties:
      FunctionName: IotEndpointProvider
      Handler: iotEndpointProvider.handler
      Runtime: nodejs6.10
      CodeUri: .
      MemorySize: 128
      Timeout: 3
      Policies:
        - Version: '2012-10-17'
          Statement:
          - Effect: Allow
            Action: 
              - iot:DescribeEndpoint
            Resource:
              - '*'
  IotEndpoint:
    Type: 'Custom::IotEndpoint'
    Properties:
      ServiceToken: !GetAtt IotEndpointProvider.Arn
Outputs:
  IotEndpointAddress:
    Value: !GetAtt IotEndpoint.IotEndpointAddress
    Export:
      Name: IotEndpointAddress

iotEndpointProvider.js

var aws = require("aws-sdk");

exports.handler = function(event, context) {
    console.log("REQUEST RECEIVED:\n" + JSON.stringify(event));

    // For Delete requests, immediately send a SUCCESS response.
    if (event.RequestType == "Delete") {
        sendResponse(event, context, "SUCCESS");
        return;
    }

    const iot = new aws.Iot();
    iot.describeEndpoint({}, (err, data) => {
    let responseData, responseStatus;
        if (err) {
            responseStatus = "FAILED";
            responseData = { Error: "describeEndpoint call failed" };
            console.log(responseData.Error + ":\n", err);
        } else  {
            responseStatus = "SUCCESS";
            responseData = { IotEndpointAddress: data.endpointAddress };
            console.log('response data: ' + JSON.stringify(responseData));
        }

        sendResponse(event, context, responseStatus, responseData);
    });
};

// Send response to the pre-signed S3 URL 
function sendResponse(event, context, responseStatus, responseData) {

    var responseBody = JSON.stringify({
        Status: responseStatus,
        Reason: "See the details in CloudWatch Log Stream: " + context.logStreamName,
        PhysicalResourceId: context.logStreamName,
        StackId: event.StackId,
        RequestId: event.RequestId,
        LogicalResourceId: event.LogicalResourceId,
        Data: responseData
    });

    console.log("RESPONSE BODY:\n", responseBody);

    var https = require("https");
    var url = require("url");

    var parsedUrl = url.parse(event.ResponseURL);
    var options = {
        hostname: parsedUrl.hostname,
        port: 443,
        path: parsedUrl.path,
        method: "PUT",
        headers: {
            "content-type": "",
            "content-length": responseBody.length
        }
    };

    console.log("SENDING RESPONSE...\n");

    var request = https.request(options, function(response) {
        console.log("STATUS: " + response.statusCode);
        console.log("HEADERS: " + JSON.stringify(response.headers));
        // Tell AWS Lambda that the function execution is done  
        context.done();
    });

    request.on("error", function(error) {
        console.log("sendResponse Error:" + error);
        // Tell AWS Lambda that the function execution is done  
        context.done();
    });

    // write data to request body
    request.write(responseBody);
    request.end();
}
like image 148
jaccus Avatar answered Oct 07 '22 16:10

jaccus


I'm afraid you cannot provision an IoT endpoint, as the only API call that is related to an IoT endpoint is DescribeEndpoint.

What you can do is create a Lambda-backed CloudFormation Custom Resource. The Lambda function will execute the DescribeEndpoint call (using the AWS SDK of your choice depending on the Lambda's runtime) and return the endpoint's URL so your other CloudFormation resources can consume it.

Here's a good example on Lambda-backed Custom Resources: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-custom-resources-lambda.html.

like image 3
spg Avatar answered Oct 07 '22 16:10

spg