-
Notifications
You must be signed in to change notification settings - Fork 4k
/
Copy pathcall-aws-service.ts
135 lines (121 loc) · 4.43 KB
/
call-aws-service.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
import { Construct } from 'constructs';
import * as iam from '../../../aws-iam';
import * as sfn from '../../../aws-stepfunctions';
import { Token } from '../../../core';
import { integrationResourceArn } from '../private/task-utils';
/**
* Properties for calling an AWS service's API action from your
* state machine.
*
* @see https://docs.aws.amazon.com/step-functions/latest/dg/supported-services-awssdk.html
*/
export interface CallAwsServiceProps extends sfn.TaskStateBaseProps {
/**
* The AWS service to call.
*
* @see https://docs.aws.amazon.com/step-functions/latest/dg/supported-services-awssdk.html
*/
readonly service: string;
/**
* The API action to call.
*
* Use camelCase.
*/
readonly action: string;
/**
* Parameters for the API action call.
*
* Use PascalCase for the parameter names.
*
* @default - no parameters
*/
readonly parameters?: { [key: string]: any };
/**
* The resources for the IAM statement that will be added to the state
* machine role's policy to allow the state machine to make the API call.
*
* By default the action for this IAM statement will be `service:action`.
*/
readonly iamResources: string[];
/**
* The action for the IAM statement that will be added to the state
* machine role's policy to allow the state machine to make the API call.
*
* Use in the case where the IAM action name does not match with the
* API service/action name, e.g. `s3:ListBuckets` requires `s3:ListAllMyBuckets`.
*
* @default - service:action
*/
readonly iamAction?: string;
/**
* Additional IAM statements that will be added to the state machine
* role's policy.
*
* Use in the case where the call requires more than a single statement to
* be executed, e.g. `rekognition:detectLabels` requires also S3 permissions
* to read the object on which it must act.
*
* @default - no additional statements are added
*/
readonly additionalIamStatements?: iam.PolicyStatement[];
}
/**
* A StepFunctions task to call an AWS service API
*/
export class CallAwsService extends sfn.TaskStateBase {
protected readonly taskMetrics?: sfn.TaskMetricsConfig;
protected readonly taskPolicies?: iam.PolicyStatement[];
constructor(scope: Construct, id: string, private readonly props: CallAwsServiceProps) {
super(scope, id, props);
if (this.props.integrationPattern === sfn.IntegrationPattern.RUN_JOB) {
throw new Error('The RUN_JOB integration pattern is not supported for CallAwsService');
}
if (!Token.isUnresolved(this.props.action) && !this.props.action.startsWith(this.props.action[0]?.toLowerCase())) {
throw new Error(`action must be camelCase, got: ${this.props.action}`);
}
if (this.props.parameters) {
const invalidKeys = Object.keys(this.props.parameters).filter(key => !key.startsWith(key[0]?.toUpperCase()));
if (invalidKeys.length) {
throw new Error(`parameter names must be PascalCase, got: ${invalidKeys.join(', ')}`);
}
}
const iamServiceMap: Record<string, string> = {
cloudwatchlogs: 'logs',
efs: 'elasticfilesystem',
elasticloadbalancingv2: 'elasticloadbalancing',
mediapackagevod: 'mediapackage-vod',
mwaa: 'airflow',
sfn: 'states',
};
const iamService = iamServiceMap[props.service] ?? props.service;
this.taskPolicies = [
new iam.PolicyStatement({
resources: props.iamResources,
// The prefix and the action name are case insensitive
// https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_action.html
actions: [props.iamAction ?? `${iamService}:${props.action}`],
}),
...props.additionalIamStatements ?? [],
];
}
/**
* @internal
*/
protected _renderTask(): any {
let service = this.props.service;
if (!Token.isUnresolved(service)) {
service = service.toLowerCase();
}
// The pattern here is: "arn:aws:states:::aws-sdk:serviceName:apiAction.[serviceIntegrationPattern]"
// See here: https://docs.aws.amazon.com/step-functions/latest/dg/supported-services-awssdk.html
// This does not change with sdk upgrades, TT:P125388388
return {
Resource: integrationResourceArn(
'aws-sdk',
`${service}:${this.props.action}`,
this.props.integrationPattern,
),
Parameters: sfn.FieldUtils.renderObject(this.props.parameters) ?? {}, // Parameters is required for aws-sdk
};
}
}