Came up as a question on Gitter:
Hi I am new to CDK and trying to "Create a Step Functions API Using API Gateway" using java as follows
RestApi api = new RestApi(this, "api");
AwsIntegrationProps awsIntegrationProps = AwsIntegrationProps.builder().withService("Step Functions")
.withAction("StartExecution").build();
AwsIntegration awsIntegration = new AwsIntegration(awsIntegrationProps);
Resource resource = api.getRoot().addResource("execution");
resource.addMethod("POST", awsIntegration);
But i am getting Error "Role ARN must be specified for AWS integrations (Service: AmazonApiGateway; Status Code: 400; Error Code: BadRequestException; Request ID: fa2f7fc8-0d5c-11e9-b2bf-ab94b16eea0c)"
How do i specify Execution Role ? Ref aws doc https://docs.aws.amazon.com/step-functions/latest/dg/tutorial-api-gateway.html
AWS integrations apparently always need a Role. The correct solution is to pass a RoleARN under the options key, but this is is validation plus a hint that we could have done locally.
Even better, we should make it as easy as on other L2 constructs to construct this required role (by doing so implicitly). We should also look into a way to set up the right permissions automatically.
Could we have an example of how this can be achieved?
Via the aws_apigateway.IntegrationOptions(credentials_role)?
or the root.add_method(options)?
I'm struggled with the same problem in typescript.
Could someone give me a hint to solve this?
@fumiyakk We did it this way, its in python but I think can be translated to any language.
class AwsStateMachineIntegration(apig.AwsIntegration):
def __init__(
self,
state_machine: StateMachine,
request_templates: typing.Optional[typing.Dict[str, str]] = None,
integration_responses: typing.Optional[typing.List[apig.IntegrationResponse]] = None,
action_parameters: typing.Optional[typing.Mapping[str, str]] = None,
integration_http_method: typing.Optional[str] = None,
path: typing.Optional[str] = None,
proxy: typing.Optional[bool] = None,
subdomain: typing.Optional[str] = None):
if not integration_responses:
integration_responses = [
apig.IntegrationResponse(
selection_pattern="200",
status_code="200",
response_templates={
"application/json": """{
"executionToken": "$input.json('$.executionArn').split(':')[7].replace('"', "")"
}"""
}
)
]
# This is a velocity template that takes all the params in the url and inject them in the body.
# it also takes the path and inject it in the body as an attribute path
if not request_templates:
request_templates = {
"application/json": """#set($allParams = $input.params())
#set($pathParams = $allParams.get('path'))
#set($inputStr = "")
#set($jsonString = $input.body.substring(1))
#foreach($pathParamName in $pathParams.keySet())
#set($inputStr = $inputStr +'"'+ $pathParamName + '": "' + $util.escapeJavaScript($pathParams.get($pathParamName)) + '"')
#if($foreach.hasNext) #set($inputStr = $inputStr + ',')#end
#end
#if($pathParams.size()>0) #set($inputStr = $inputStr + ",") #end
{"input": "{$util.escapeJavaScript($inputStr) $util.escapeJavaScript($jsonString)",
"stateMachineArn": \"""" + state_machine.state_machine_arn
+ """"
}"""
}
start_execution_role = self._get_start_execution_role(state_machine)
super().__init__(
service="states",
action="StartExecution",
action_parameters=action_parameters,
integration_http_method=integration_http_method,
options=IntegrationOptions(
credentials_role=start_execution_role,
integration_responses=integration_responses,
request_templates=request_templates,
passthrough_behavior=apig.PassthroughBehavior.NEVER
),
path=path,
proxy=proxy,
subdomain=subdomain)
@staticmethod
def _get_start_execution_role(
state_machine: StateMachine) -> iam.Role:
start_execution_policy = iam.Policy(
scope=state_machine,
id=f"Start{state_machine.node.id}Policy",
statements=[
iam.PolicyStatement(
actions=[step_functions.Action.START_EXECUTION.value],
resources=[state_machine.state_machine_arn])])
start_execution_role = iam.Role(
scope=state_machine,
id=f"{state_machine.node.id}ExecutionRole",
assumed_by=iam.ServicePrincipal(
service="apigateway.amazonaws.com"))
start_execution_role.attach_inline_policy(
start_execution_policy)
return start_execution_role
I don't have too much time to explain it all at once, but hope it helps. Either way if you don't understand something let me know!
@jindriago-scf Thank you for helping!
I could understand your code and achieved implimenting in TypeScript.
@fumiyakk Any chance you could share your code? :)
@Awlsring
Sorry. I couldn't show the raw code because it's for work.
So, I made some abstract code. It might be broken.
I hope this could help you.
import * as cdk from '@aws-cdk/core';
import * as apigw from '@aws-cdk/aws-apigateway';
import * as iam from '@aws-cdk/aws-iam';
type Arns = { [key: string]: string }
export class SampleApiGatewayStack extends cdk.Stack {
constructor(scope: cdk.Construct, id: string, Arns: Arns, props?: cdk.StackProps) {
super(scope, id, props);
const api = new apigw.RestApi(this, 'Api', {
restApiName: 'stateApiName',
});
const sampleRole = new iam.Role(this, 'StepFunctionRole',{
assumedBy: new iam.ServicePrincipal('apigateway.amazonaws.com')
});
sampleRole.addToPolicy(new iam.PolicyStatement({
resources: ['*'],
actions: ["states:*"]
}));
// Get StepFunctionArn
const str1: string = "#set($inputRoot = $input.path('$')) {"
const str2: string = `"input": "$util.escapeJavaScript($input.json('$'))",`
const str3: string = `"stateMachineArn":"`
const str4: string = `"}`
const templateString: string = str1 + str2 + str3 + Arns["stateMachineArn"] + str4
const requestTemplates = {
"application/json": templateString
}
// Connect stateMachine with api gateway
const stateMachineIntegration = new apigw.AwsIntegration({
service: "states",
action: "sampleAction",
options: {
credentialsRole: sampleRole,
requestTemplates: requestTemplates,
}
});
const process1 = api.root.addResource('preprocessing')
const process2 = process1.addResource('v1')
process2.addMethod('POST', stateMachineIntegration, {
methodResponses: [{
statusCode: '200',
}]
});
}
}
Most helpful comment
@jindriago-scf Thank you for helping!
I could understand your code and achieved implimenting in TypeScript.