Step Function with EventBridge task `"NotAuthorizedForSourceException"` - amazon-web-services

Background
I'm creating a Step Function state machine that starts an AWS CodeBuild once a defined AWS CodePipeline has an execution status of SUCCEED. I'm using the .waitForTaskToken feature within the Step Function to wait on the CodePipeline to succeed via a CloudWatch event. Once the pipeline succeeds, the event sends back the token to the step function and runs the CodeBuild.
Here's the step function definition:
{
"StartAt": "PollCP",
"States": {
"PollCP": {
"Next": "UpdateCP",
"Parameters": {
"Entries": [
{
"Detail": {
"Pipeline": [
"bar-pipeline"
],
"State": [
"SUCCEEDED"
],
"TaskToken.$": "$$.Task.Token"
},
"DetailType": "CodePipeline Pipeline Execution State Change",
"Source": "aws.codepipeline"
}
]
},
"Resource": "arn:aws:states:::events:putEvents.waitForTaskToken",
"Type": "Task"
},
"UpdateCP": {
"End": true,
"Parameters": {
"ProjectName": "foo-project"
},
"Resource": "arn:aws:states:::codebuild:startBuild.sync",
"Type": "Task"
}
}
}
The permissions for the step function:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Action": "codebuild:StartBuild",
"Resource": "*"
},
{
"Sid": "",
"Effect": "Allow",
"Action": "codepipeline:*",
"Resource": "*"
}
]
}
and arn:aws:iam::aws:policy/CloudWatchEventsFullAccess
Problem
The cloudwatch event within the step function returns the error:
Error
EventBridge.FailedEntry
Cause
{
"Entries": [
{
"ErrorCode": "NotAuthorizedForSourceException",
"ErrorMessage": "Not authorized for the source."
}
],
"FailedEntryCount": 1
}
Attempts:
Modify the associated Codepipeline and Codebuild role to have step function permission to send task statuses. Specifically, the permission is:
{
"Effect": "Allow",
"Action": [
"states:SendTaskSuccess",
"states:SendTaskFailure",
"states:SendTaskHeartbeat"
],
"Resource": "*"
}
Got the same original error mentioned above.
Modify the associated Step Function machine's permission to have full access to all step function actions and resources. Got the same original error mentioned above.
Test the event rule specified in the PollCP step function task with the default AWS EventBridge bus. The event was:
{
"version": "0",
"detail-type": "CodePipeline Pipeline Execution State Change",
"source": "aws.codepipeline",
"account": "123456789012",
"time": "2021-06-14T00:44:41Z",
"region": "us-west-2",
"resources": [],
"detail": {
"pipeline": "<pipeline-arn>",
"state": "SUCCEED"
}
}
The event outputted the same error mentioned above. This probably means the error is strictly related to the event entry mentioned in the code snippet above.

Are you trying to trigger your State Machine using CloudWatch event when a CodePipeline pipeline completes/succeeds?
If so, you cannot define your trigger in your state machine.
The integration with EventBridge is not so that state machine can be triggered by events. Rather it is to Publish events to an event bus from your state machine or workflow.
Read more here: https://aws.amazon.com/blogs/compute/introducing-the-amazon-eventbridge-service-integration-for-aws-step-functions/
So I suggest you create a CloudWatch rule and target your state machine instead.
If you want to use the waitForTaskToken pattern. You will have to explicitly return that token with a send_task_success API call (sample below for python/boto3).
sfn.send_task_success(
taskToken=task_token,
output=json.dumps(some_optional_payload)
)
This means that, when the step executes, it will publish the event to EventBridge bus. You will have to detect this event outside your state machine, most likely with an CloudWatch event rule. Then trigger a lambda function from the rule. The lambda function performs the send_task_success API call which restarts/continues your workflow/state machine.
In my opinion, that is just unnecessary. Like I said, you can simply watch for pipeline execution state changes uing CW event rule, trigger your state machine, and your state machine starts with the CodeBuild stage.
Side note: It's nice to see people using Step Functions for CI/CD pipeline. It's just got more flexibility and ability to do complex branching strategies. Will probably do a blog post around this soon.

Your CodeBuild service role will need permission to use the states:SendTask* (Success, Failure, and Heartbeat) actions so that it can notify the state machine. This page in the docs has more details.

Related

AWS: Trigger step function state machine on s3 object creation using Event Bridge Not Working

I enabled notifications for Amazon EventBridge on my s3 bucket.
Then I created an EventBridge rule with the following event pattern:
{
"detail": {
"bucket": {
"name": ["arn:aws:s3:::my-bucket"]
}
},
"detail-type": ["Object Created"],
"source": ["aws.s3"]
}
Then I added my state machine as the target of this rule. I also attached an IAM role with the following policy for this event target.
"Statement": [
{
"Effect": "Allow",
"Action": [ "states:StartExecution" ],
"Resource": [ "arn:aws:states:*:*:stateMachine:*" ]
}
]
Then I attached the following policy to my state machine step function as well:
{
"Action": "events:*",
"Resource": "arn:aws:events:us-east-1:my-account-id:event-bus/default",
"Effect": "Allow"
}
After doing all of this, still my state machine is not getting invoked.
What am I missing here? How can I debug where the issue might be?
Have you checked if your custom pattern matches the event?
I think you do not need arn in the name.
Try with
{
"detail": {
"bucket": {
"name": ["my-bucket"]
}
},
"detail-type": ["Object Created"],
"source": ["aws.s3"]
}

CDK Unable to Add CodeStarNotification to CodePipeline

I use CDK to deploy a codepipeline. It works fine until I try to add notification for codepipeline success/fail events. It gives CREATE_FAILED error with message Resource handler returned message: "Invalid request provided: AWS::CodeStarNotifications::NotificationRule" (RequestToken: bb566fd0-1ac9-5d61-03fe-f9c27b4196fa, HandlerErrorCode: InvalidRequest). What could be the reason? Thanks.
import * as codepipeline from "#aws-cdk/aws-codepipeline";
import * as codepipeline_actions from "#aws-cdk/aws-codepipeline-actions";
import * as codestar_noti from "#aws-cdk/aws-codestarnotifications";
import * as sns from "#aws-cdk/aws-sns";
const pipeline = new codepipeline.Pipeline(...);
const topicArn = props.sns_arn_for_developer;
const targetTopic = sns.Topic.fromTopicArn(
this,
"sns-notification-topic",
topicArn
);
new codestar_noti.NotificationRule(this, "Notification", {
detailType: codestar_noti.DetailType.BASIC,
events: [
"codepipeline-pipeline-pipeline-execution-started",
"codepipeline-pipeline-pipeline-execution-failed",
"codepipeline-pipeline-pipeline-execution-succeeded",
"codepipeline-pipeline-pipeline-execution-canceled",
],
source: pipeline,
targets: [targetTopic],
});
Here is the snippet of generated cloudformation tempalte.
"Notification2267453E": {
"Type": "AWS::CodeStarNotifications::NotificationRule",
"Properties": {
"DetailType": "BASIC",
"EventTypeIds": [
"codepipeline-pipeline-pipeline-execution-started",
"codepipeline-pipeline-pipeline-execution-failed",
"codepipeline-pipeline-pipeline-execution-succeeded",
"codepipeline-pipeline-pipeline-execution-canceled"
],
"Name": "sagemakerbringyourownNotification36194CEC",
"Resource": {
"Fn::Join": [
"",
[
"arn:",
{
"Ref": "AWS::Partition"
},
":codepipeline:ap-southeast-1:305326993135:",
{
"Ref": "sagemakerbringyourownpipeline0A8C43B1"
}
]
]
},
"Targets": [
{
"TargetAddress": "arn:aws:sns:ap-southeast-1:305326993135:whitespace_alerts",
"TargetType": "SNS"
}
]
},
"Metadata": {
"aws:cdk:path": "sagemaker-bring-your-own/Notification/Resource"
}
},
FWIW, I got the exact same error "Invalid request provided: AWS::CodeStarNotifications::NotificationRule" from a CDK app where the Topic was created (not imported). It turned out to be a transient issue, because it succeeded the second time without any changes. I suspect it was due to a very large ECR image which was build the first time as part of the deploy and which took quite some time. My speculation is that the Topic timed out and got into some kind of weird state waiting for the NotificationRule to be created.
This is because imported resources cannot be modified. As you pointed out in the comments, setting up the notification involves modifying the Topic resource, specifically its access policy.
Reference: https://docs.aws.amazon.com/cdk/v2/guide/resources.html#resources_importing
I was able to solve this by doing the following in that order:
First removing the below statement from the resource policy of the SNS topic.
Then deploying the stack(which interestingly doesn't add anything to the resource policy)
Once the stack deployment finishes, update the resource policy manually to add the below statement.
{
"Sid": "AWSCodeStarNotifications_publish",
"Effect": "Allow",
"Principal": {
"Service": "codestar-notifications.amazonaws.com"
},
"Action": "SNS:Publish",
"Resource": "arn:aws:sns:ap-south-1:xxxxxxxxx:test"
}

AWS invoke cross account lambda via Cloudwatch Event Bus

I have a AWS lambda function deployed in multiple accounts. I'm looking for a way to schedule to trigger these lambda function from master account via Cloudwatch Event Bus. Is this possible?
In line with what #amitd is suggesting you need to implement something like this (Using EventBridge , EventBus).
To configure cross-account event bridge communication following needs to be done. I am providing sample events and filters, you can replace the event and filters as per requirement.
Steps to be performed on Account B: Receiver account
Create an event bus named event-bus-b. Put the resource-based policy as shown below.
{
"Version": "2012-10-17",
"Statement": [{
"Sid": "WebStoreCrossAccountPublish",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::<account-A>:root"
},
"Action": "events:PutEvents",
"Resource": "arn:aws:events:<your-region>:<Account-B>:event-bus/event-bus-b"
}]
}
Create a rule in account B let's calls it eb-rule-b. In this Rule select event-bus-b as a source event bus.
Provision following event filter pattern:
Event pattern:
{
"detail-type": [
"uoe"
],
"source": [
"somesource"
]
}
Also, test the pattern using the test event.
Test Event:
{
"version": "0",
"id": "55fghj-89a9-a0b3-1ccb-79c25c7d6cd2",
"detail-type": "uoe",
"source": "somesource",
"account": "<ACCOUNT_ID>",
"time": "2020-04-24T13:53:21Z",
"region": "<YOUR_REGION>",
"resources": [],
"detail": {
"userOrg" : "OrgName"
}
}
Select the event bus event-bus-b in the drop-down.
Select the target "Lambda"
Put the ARN of the event bus which you have created in Account B.
arn:aws:lambda:<your-region>:<AccountB>:function:<AccountBLambda>
Also check on the check box "Create a new role for this specific resource". This will create a role in account A which enables lambda execution.
Click on create and create the rule.
Now click on the event bus event-bus-a and click on Send events button.
Send a dummy event as shown below and validate that the communication between event bus and the lambda in account B is all ok.
If you face some issue in this plumbing refer to :https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-troubleshooting.html#eb-lam-function-not-invoked
Once we are good in Account B ( i.e we are able to invoke the lambda by sending events on the event bus, configure other accounts by following the same steps.
Steps to be performed on Account A: Sender account
Create an event bus event-bus-a in account A.
Create a rule eb-rule-a in account A with the following details:
Event pattern:
{
"detail-type": [
"uoe"
],
"source": [
"somesource"
]
}
Also, test the pattern using the test event.
Test Event:
{
"version": "0",
"id": "55fghj-89a9-a0b3-1ccb-79c25c7d6cd2",
"detail-type": "uoe",
"source": "somesource",
"account": "<ACCOUNT_ID>",
"time": "2020-04-24T13:53:21Z",
"region": "<YOUR_REGION>",
"resources": [],
"detail": {
"userOrg" : "OrgName"
}
}
Select the event bus event-bus-a in the drop-down.
Select the target "Event bus in different account or Region"
Put the ARN of the event bus which you have created in Account B.
arn:aws:events:<your-region>:<Account-B>:event-bus/event-bus-b
Also check on the check box "Create a new role for this specific resource". This will create a role in account A which enables the users in account A to publish on account b event bus. The below policy is auto-created and you don't need to do anything.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"events:PutEvents"
],
"Resource": [
arn:aws:events:<your-region>:<Account-B>:event-bus/event-bus-b
]
}
]
}
Click on create and create the rule.
Now click on the event bus event-bus-a and click on Send events button.
Provide details and click on send.
Sample event:
{
"version": "0",
"id": "55fghj-89a9-a0b3-1ccb-79c25c7d6cd2",
"detail-type": "uoe",
"source": "somesource",
"account": "<ACCOUNT_ID>",
"time": "2020-04-24T13:53:21Z",
"region": "<YOUR_REGION>",
"resources": [],
"detail": {
"userOrg" : "OrgName"
}
}
Event will propagate to the event bus defined in account B.
Repete from steps 4- 10 for all other accounts ( i.e create multiple targets in the same rule).
Once configured a single event in Account A will propagates to multiple accounts and you will achieve the necessary fanning.
Please refer following options and related documentation from AWS;
Using CloudWatchEvents:
a. Sending and Receiving Events Between AWS Accounts
b. Cross-Account Delivery of CloudWatch Events
OR
Using Amazon EventBridge:
a. Simplifying cross-account access with Amazon EventBridge
b. Sending and recieving Amazon EventBridge events between AWS accounts

Disable/Enable Lambda SNS Trigger Programmatically

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.
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.
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.

My CloudWatch Event rule doesn't trigger my CodePipeline pipeline

I'm having some issues with AWS CloudWatch Events.
I'm creating a CodePipeline CI pipeline which have a CodeCommit repository as the Source, a CodeBuild project as the Build/Test phase (then, it deploys to Lambda, but the problem isn't there).
We have multiple projects and we are going to push multiple other projects. So, I created a script that manages the AWS CI stuff (i.e. creating a pipeline, a CodeBuild project, ... AND a CloudWatch Events rule, linked to the pipeline).
The first time I push my code, it works. But then, the process stop getting triggered by the push on CodeCommit.
I found a solution (but NOT the one I want) : I just have to modify the pipeline, modify the stage (Source), not touching anything, and saving the null modification : and it works (before saving, it ask the authorization to create a CloudWatch Events rule associated with this pipeline).
Does somebody encountered this issue ? What did you do to bypass it ?
I really want to make a 100% automated CI, I don't want to go to the AWS Console each time my team create a new repository or push a new branch on an existing repository.
EDIT :
Here is the JSON of my CloudWatch Events rule :
{
"Name": "company-ci_codepipeline_project-stage",
"EventPattern": "cf. second JSON",
"State": "ENABLED",
"Arn": "arn:aws:events:region:xxx:rule/company-ci_codepipeline_project-stage",
"Description": "CloudWatch Events rule to automatically trigger the needed pipeline from every push to project repository, on the stage branch on CodeCommit."
}
And here is the EventPattern JSON :
{
"source": [
"aws.codecommit"
],
"detail-type": [
"CodeCommit repository state change"
],
"resources": [
"arn:aws:codecommit:region:xxx:project"
],
"detail": {
"event": [
"referenceCreated",
"referenceUpdated"
],
"referenceType": [
"branch"
],
"referenceName": [
"stage"
]
}
}
I've found this issue is typically related to the event rule/target/role configuration. If you don't have a target associated with your rule, you will NOT see the event invoked when reviewing metrics. Since your EventPattern looks correct, I'm thinking the target might be your issue.
You should have a configured target that looks something like:
{
"Rule": "company-ci_codepipeline_project-stage",
"Targets": [
{
"RoleArn": "arn:aws:iam::xxx:role/cwe-codepipeline",
"Id": "ProjectPipelineTarget",
"Arn": "arn:aws:codepipeline:region:xxx:your-pipeline"
}
]
}
If that seems all good, I'd next check that the role associated with the target is granting the correct permissions. My role looks something like:
{
"Role": {
"Description": "Allows CloudWatch Events to invoke targets and perform actions in built-in targets on your behalf.",
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": "sts:AssumeRole",
"Principal": {
"Service": "events.amazonaws.com"
},
"Effect": "Allow",
"Sid": ""
}
]
},
"MaxSessionDuration": 3600,
"RoleId": "xxxx",
"CreateDate": "2018-08-06T20:56:19Z",
"RoleName": "cwe-codepipeline",
"Path": "/",
"Arn": "arn:aws:iam::xxx:role/cwe-codepipeline"
}
}
And it has an inline policy of:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"codepipeline:StartPipelineExecution"
],
"Resource": [
"arn:aws:codepipeline:*:xxx:*"
]
}
]
}
For reference, check out this documentation