-
Notifications
You must be signed in to change notification settings - Fork 3.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(stepfunctions-tasks): step functions task for cross-region AWS API call #30061
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The pull request linter has failed. See the aws-cdk-automation comment below for failure reasons. If you believe this pull request should receive an exemption, please comment and provide a justification.
A comment requesting an exemption should contain the text Exemption Request
. Additionally, if clarification is needed add Clarification Request
to a comment.
✅ Updated pull request passes all PRLinter validations. Dismissing previous PRLinter review.
...k/custom-resource-handlers/lib/aws-stepfunctions-tasks/cross-region-aws-sdk-handler/index.ts
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good overall, just some concerns and minor comments. I'm also wondering if it's better for maintainability not to create a new class, and to reuse the existing CallAwsService
, and just to add an optional crossRegionTarget
prop
...k/custom-resource-handlers/lib/aws-stepfunctions-tasks/cross-region-aws-sdk-handler/index.ts
Outdated
Show resolved
Hide resolved
...mework-integ/test/aws-stepfunctions-tasks/test/lambda/integ.call-aws-service-cross-region.ts
Outdated
Show resolved
Hide resolved
...k/custom-resource-handlers/lib/aws-stepfunctions-tasks/cross-region-aws-sdk-handler/index.ts
Outdated
Show resolved
Hide resolved
|
||
try { | ||
// esbuild-disable unsupported-require-call -- not esbuildable but that's fine | ||
const pkg = require(`@aws-sdk/client-${event.service}`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct me if I'm wrong, but normalizeServiceName
is not being ran for your handler? It would allow us to keep the same pattern as other service
s props, see AwsSdkCall:
this.service = normalizeServiceName(service); |
export function normalizeServiceName(service: string) { | |
service = service.toLowerCase(); // Lowercase | |
service = service.replace(/^@aws-sdk\/client-/, ''); // Strip the start of a V3 package name | |
service = v2ToV3Mapping()?.[service] ?? service; // Optionally map v2 name -> v3 name | |
return service; | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd like to keep this feature and API as simple as possible to lower the maintenance cost. AwsSdkCall
code has become such complicated mostly for backward compatibility reason. Since this feature is new, we don't have to introduce the complexity at all.
Also the API is aimed to be compatible with the existing CallAwsService
construct (and it should), which also does not support normalizing service names.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll let the maintainer weigh in, I think it's a nice piece of QoL, but I understand your maintainability concern
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To help me understand, can you elaborate on your concern? What maintenance cost are you concerned with using AwsSdkCall code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AwsSdkCall's code is mainly to solve the v2-v3 compatibility problem. Since this construct does not require such compatibility, I think there is little advantage to using the existing code. After all, the new Lambda is only code under 100 lines. Also, if we want to reuse the existing code, we need to refactor it first (for example, currently it's only for CFn custom resource handler, but this one is for invocation from SFn). Given the ROI, I'd rather keep them independent.
*/ | ||
export interface CallAwsServiceCrossRegionProps extends sfn.TaskStateBaseProps { | ||
/** | ||
* The AWS service to call in AWS SDK for JavaScript v3 style. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See comment about normalizeServiceName
// esbuild-disable unsupported-require-call -- not esbuildable but that's fine | ||
const pkg = require(`@aws-sdk/client-${event.service}`); | ||
const Client = findV3ClientConstructor(pkg); | ||
const Command = findCommandClass(pkg, event.action); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similarly, you should be able to use normalizeActionName
, see AwsSdkCall:
export function normalizeServiceName(service: string) { | |
service = service.toLowerCase(); // Lowercase | |
service = service.replace(/^@aws-sdk\/client-/, ''); // Strip the start of a V3 package name | |
service = v2ToV3Mapping()?.[service] ?? service; // Optionally map v2 name -> v3 name | |
return service; | |
} | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as service name, I'd like to intentionally only support action names in camelCase.
/** | ||
* The API action to call. | ||
* | ||
* Use camelCase. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See comment about normalizeActionName
packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/lambda/call-aws-service-cross-region.ts
Outdated
Show resolved
Hide resolved
Hi @nmussy, thanks for the comments.
I considered this approach as well but chose the current approach, because with the current approach we can easily avoid from future breaking changes when SFn will officially start to support cross-region API call; we can just deprecate |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, just a couple of pending comments
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left some minor feedbacks. Also have a high-level questions I want to clarify on. There's already a stepfunctions task for CallAwsService
. Is the reason of building this cross-region-aws-sdk-handler
using custom resource lambda function because the AWS SDK service integrations do not support cross region invocation?
packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/lambda/call-aws-service-cross-region.ts
Outdated
Show resolved
Hide resolved
* | ||
* @default - service:action | ||
*/ | ||
readonly iamAction?: string; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Question: does it only support one single action? What would users do if I want to support multiple actions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In that case, you can use additionalIamStatements
. I agree that this should ideally be iamActions?: string[]
, but then the API becomes less compatible with CallAwsService
construct. I think it's okay to leave this as-is because most API only requires a single IAM action anyway. Wdyt?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you share an example how to use additionalIamStatements
with another action? I also think it would be nice to add this example to the readme file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, but that is same as CallAwsService, which is described here:
aws-cdk/packages/aws-cdk-lib/aws-stepfunctions-tasks/README.md
Lines 233 to 245 in c826d8f
```ts | |
const detectLabels = new tasks.CallAwsService(this, 'DetectLabels', { | |
service: 'rekognition', | |
action: 'detectLabels', | |
iamResources: ['*'], | |
additionalIamStatements: [ | |
new iam.PolicyStatement({ | |
actions: ['s3:getObject'], | |
resources: ['arn:aws:s3:::my-bucket/*'], | |
}), | |
], | |
}); | |
``` |
so I added a line as below:
Other properties such as
additionalIamStatements
can be used in the same way as theCallAwsService
task.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good, thank you!
packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/lambda/call-aws-service-cross-region.ts
Show resolved
Hide resolved
packages/aws-cdk-lib/aws-stepfunctions-tasks/lib/lambda/call-aws-service-cross-region.ts
Show resolved
Hide resolved
|
||
try { | ||
// esbuild-disable unsupported-require-call -- not esbuildable but that's fine | ||
const pkg = require(`@aws-sdk/client-${event.service}`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To help me understand, can you elaborate on your concern? What maintenance cost are you concerned with using AwsSdkCall code.
Hi @GavinZZ, thank you so much for the review!
Yes. According to the doc:
|
Thank you for contributing! Your pull request will be updated from main and then merged automatically (do not update manually, and be sure to allow changes to be pushed to your fork). |
Thank you for contributing! Your pull request will be updated from main and then merged automatically (do not update manually, and be sure to allow changes to be pushed to your fork). |
Thank you for contributing! Your pull request will be updated from main and then merged automatically (do not update manually, and be sure to allow changes to be pushed to your fork). |
AWS CodeBuild CI Report
Powered by github-codebuild-logs, available on the AWS Serverless Application Repository |
Thank you for contributing! Your pull request will be updated from main and then merged automatically (do not update manually, and be sure to allow changes to be pushed to your fork). |
Comments on closed issues and PRs are hard for our team to see. If you need help, please open a new issue that references this one. |
Issue # (if applicable)
Closes #29918.
Reason for this change
It would be useful if we could call AWS API across regions from a Step Functions state machine. Currently it is not officially supported even with AWS SDK integration tasks.
Our usecase is to automate a cross-region failover scenario in a multi-region application. This requires you to orchestrate multiple API calls for both active and standby regions (e.g. failover Aurora DB cluster, rewrite AppConfig parameter, etc), and it would be great if we can manage these operations in a single state machine.
Description of changes
This PR adds a new construct
CallAwsServiceCrossRegion
that deploys 1. a Lambda function to call AWS API in different regions 2. SFn task to call the function.Because most properties are compatible with the existing
CallAwsService
construct, you can use the new construct by just adding theregion
property.Additionally, it also allows to set
endpoint
to override AWS API endpoint, because some AWS APIs requires you to override it. (e.g. Route53 ARC)Here's example code to define this new SFn task:
Description of how you validated changes
Added unit tests and integ tests.
Checklist
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license