AWS JavaScript TypeScript

Gotcha when using AWS Lambda and IoT cross-region

This past weekend I wasted a few hours trying to figure out why my AWS Lambda was failing to talk to the IoT Core service. This was despite temporarily adding an IAM policy to allow all “iot:*” actions, as well as creating a fully permissive IoT Policy.

My lambda is operating in the us-east-2 region, whereas the IoT thing was defined in us-west-2. My code looked approximately like this:

const iotdata = new IotData({                              
    endpoint: ''
const params = {                                           
    topic: `cool/topic`,                                   
    payload: JSON.stringify({"this": "is data, buddy!"})   
const response = await iotdata.publish(params).promise();  

The reported error message in CloudWatch was this:

ERROR	ForbiddenException: Forbidden
    at Object.extractError (/var/runtime/node_modules/aws-sdk/lib/protocol/json.js:52:27)
    at Request.extractError (/var/runtime/node_modules/aws-sdk/lib/protocol/rest_json.js:55:8)
    at Request.callListeners (/var/runtime/node_modules/aws-sdk/lib/sequential_executor.js:106:20)
    at Request.emit (/var/runtime/node_modules/aws-sdk/lib/sequential_executor.js:78:10)
    at Request.emit (/var/runtime/node_modules/aws-sdk/lib/request.js:688:14)
    at Request.transition (/var/runtime/node_modules/aws-sdk/lib/request.js:22:10)
    at AcceptorStateMachine.runTo (/var/runtime/node_modules/aws-sdk/lib/state_machine.js:14:12)
    at /var/runtime/node_modules/aws-sdk/lib/state_machine.js:26:10
    at Request.<anonymous> (/var/runtime/node_modules/aws-sdk/lib/request.js:38:9)
    at Request.<anonymous> (/var/runtime/node_modules/aws-sdk/lib/request.js:690:12) {
  code: 'ForbiddenException',
  time: 2021-08-31T01:01:11.987Z,
  requestId: '...',
  statusCode: 403,
  retryable: false,
  retryDelay: 71.34502878897193

Not very helpful, but Googling does suggest that the 403 status code means there is a problem with IAM. But as you can guess from the setup of this post, that was not the issue.

It turns out that even though the ‘endpoint’ specifies the region, you still need to pass the ‘region’ argument when constructing IotData (for cross-region usage):

const iotdata = new IotData({                              
    endpoint: '',
    region: 'us-west-2'

In retrospect this should have been obvious and perhaps experienced AWS developers are thinking “well, duh”. But maybe this can help someone Googling trying to solve a similar problem.

Leave a Reply