Disable/Enable Lambda SNS Trigger Programmatically

2019-08-03 17:03发布

问题:

I need to programmatically disable a lambda's SNS trigger, however, I seem to be unable to do so. I want this to show "Disabled" in the AWS Lambda console for the function:

Here's the code I've tried:

function updateEndpoints(endpoints, enable) {
    const promises = [];
    endpoints.forEach((endpoint) => {
        console.log(`${enable ? 'Enabling' : 'Disabling'} Endpoint: ${endpoint}`);
        promises.push(
            SNS.setEndpointAttributes({
                EndpointArn: endpoint,
                Attributes: {
                    Enabled: enable ? 'True' : 'False',
                },
            }).promise()
            .catch((e) => {
                console.error(`Error ${enable ? 'Enabling' : 'Disabling'} Endpoint: ${endpoint}`);
                console.error(e);
            }));
    });

    return Promise.all(promises);
}

The endpoint ARN is passed in correctly with a string like (with correct values in place of the <> below):

-
arn:aws:lambda:<region>:<accountId>:function:<functionName>
-

This produces an error from AWS for each endpoint I try to enable or disable:

-
InvalidParameter: Invalid parameter: EndpointArn Reason: Vendor lambda is not of SNS
-

Is it not possible to disable the trigger/endpoint for a lambda via SNS? How would one go about doing this? I would prefer not to have to unsubscribe/subscribe as this would take the subscription objects out of CloudFormation's scope (correct?). I looked at updateEventSourceMappings, however, per the documentation, that only works with DynamoDB streams, Kinesis Streams, and SQS -- not SNS.

回答1:

It appears that there is no capability to "disable" a Lambda subscription to an SNS topic.

I base my reasoning on the follow steps I took:

  • Created an AWS Lambda function
  • Created an Amazon SNS topic
  • Subscribed the Lambda function to the SNS topic (done via the SNS console)
  • Confirmed in the Lambda console that the function subscription to SNS is "enabled"
  • Ran aws sns list-subscriptions-by-topic --topic-arn arn:aws:sns:ap-southeast-2:123456789012:my-topic
  • Saw that the Lambda function was subscribed

The response was:

{
    "Subscriptions": [
        {
            "SubscriptionArn": "arn:aws:sns:ap-southeast-2:123456789012:stack:...",
            "Owner": "123456789012",
            "Protocol": "lambda",
            "Endpoint": "arn:aws:lambda:ap-southeast-2:743112987576:function:my-function",
            "TopicArn": "arn:aws:sns:ap-southeast-2:123456789012:stack"
        }
    ]
}

I then disabled the trigger in the Lambda console and saved the Lambda function. When I re-ran the above command, the results were empty:

{
    "Subscriptions": []
}

When I enabled it again, the subscription returned.

So, my assumption is that, since the "disable/enable" button actually adds and removes a subscription, there does not appear to be any capability to 'disable' a subscription.



回答2:

I found the (100%) correct way to do this. While the answer from @John Rotenstein could be used, it's not quite right, but should still work.

I found when you click the toggle, the lambda's policy is actually updated:

Enabled:

{
  "Version": "2012-10-17",
  "Id": "default",
  "Statement": [
    {
      "Sid": "my-lambda-1552674933742",
      "Effect": "Allow",
      "Principal": {
        "Service": "sns.amazonaws.com"
      },
      "Action": "lambda:InvokeFunction",
      "Resource": "arn:aws:lambda:us-west-2:1234567890:function:my-lambda",
      "Condition": {
        "ArnLike": {
          "AWS:SourceArn": "arn:aws:sns:us-west-2:1234567890:my-lambda"
        }
      }
    }
  ]
}

Disabled:

{
  "Version": "2012-10-17",
  "Id": "default",
  "Statement": [
    {
      "Sid": "my-lambda-1552674933742",
      "Effect": "Allow",
      "Principal": {
        "Service": "sns.amazonaws.com"
      },
      "Action": "lambda:DisableInvokeFunction",
      "Resource": "arn:aws:lambda:us-west-2:1234567890:function:my-lambda",
      "Condition": {
        "ArnLike": {
          "AWS:SourceArn": "arn:aws:sns:us-west-2:1234567890:my-lambda"
        }
      }
    }
  ]
}

Notice Action is lambda:InvokeFunction vs. lambda:DisableInvokeFunction.

My process to do this is as follows: - Lambda.listFunctions - for each function, Lambda.removePermission - for each function, Lambda.addPermission

Notes:

  • the Lambda api has a default safety throttle of 100 concurrent executions per account per region.
  • You can only update resource-based policies for Lambda resources within the scope of the AddPermission and AddLayerVersionPermission API actions. You can't author policies for your Lambda resources in JSON, or use conditions that don't map to parameters for those actions. See docs here

Also, you can use Lambda.getPolicy to see the policy of the lambda to ensure it is updated.