Разверните StepFunctions с CloudFormation из файла внешнего определения
Я пытаюсь развернуть пошаговые функции с CloudFormation, и я хотел бы сослаться на фактическое определение пошаговой функции из внешнего файла в S3.
Вот как выглядит шаблон:
StepFunction1:
Type: "AWS::StepFunctions::StateMachine"
Properties:
StateMachineName: !Ref StepFunction1SampleName
RoleArn: !GetAtt StepFunctionExecutionRole.Arn
DefinitionString:
Fn::Transform:
Name: AWS::Include
Parameters:
Location:
Fn::Sub: 's3://${ArtifactsBucketName}/StepFunctions/StepFunction1/definition.json'
Тем не менее, это не поддерживается, так как мы получаем ошибку
Property validation failure: [Value of property {/DefinitionString} does not match type {String}]
Я делаю нечто подобное для API, ссылаясь на фактическое определение API из внешнего файла чванства, и это, кажется, работает нормально.
Пример:
SearchAPI:
Type: "AWS::Serverless::Api"
Properties:
Name: myAPI
StageName: latest
DefinitionBody:
Fn::Transform:
Name: AWS::Include
Parameters:
Location:
Fn::Sub: 's3://${ArtifactsBucketName}/ApiGateway/myAPI/swagger.yaml'
Как я могу сделать эту работу?
2 ответа
Хитрость заключается в том, чтобы избежать свойства StepFunction DefinitionString и включить фактическое свойство DefinitionString во внешний файл, на который ссылается CloudFormation. Экранирование только строки определения stepfunction приведет к сбою, CloudFormation пожаловалась, что указанный шаблон Transform/Include не является допустимым yaml/json. Вот как это выглядит: Шаблон:
StepFunction1:
Type: "AWS::StepFunctions::StateMachine"
Properties:
StateMachineName: !Ref StepFunction1SampleName
RoleArn: !GetAtt StepFunctionExecutionRole.Arn
Fn::Transform:
Name: AWS::Include
Parameters:
Location:
Fn::Sub: 's3://${ArtifactsBucketName}/StepFunctions/StepFunction1/definition.json'
Файл определения внешней пошаговой функции:
{
"DefinitionString" : {"Fn::Sub" : "{\r\n \"Comment\": \"A Retry example of the Amazon States Language using an AWS Lambda Function\",\r\n \"StartAt\": \"HelloWorld\",\r\n \"States\": {\r\n \"HelloWorld\": {\r\n \"Type\": \"Task\",\r\n \"Resource\": \"arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${HelloWorldLambdaFunctionName}\", \r\n \"End\": true\r\n }\r\n }\r\n}"}
}
Теперь, хотя это решает проблему, немного сложнее поддерживать определение StepFunction, в этой форме, в управлении исходным кодом.
Поэтому я подумал об использовании пользовательского ресурса CloudFormation, поддерживаемого лямбда-функцией. Лямбда-функция будет иметь дело с фактической экранирующей частью StepFunction DefinitionString.
Вот как это выглядит: Шаблон:
StepFunctionParser:
Type: Custom::AMIInfo
Properties:
ServiceToken: myLambdaArn
DefinitionString:
Fn::Transform:
Name: AWS::Include
Parameters:
Location:
Fn::Sub: 's3://${ArtifactsBucketName}/StepFunctions/StepFunctionX/definition.json'
StepFunctionX:
Type: "AWS::StepFunctions::StateMachine"
Properties:
StateMachineName: StepFunction1SampleNameX
RoleArn: !GetAtt StepFunctionExecutionRole.Arn
DefinitionString: !GetAtt StepFunctionParser.DefinitionString
Файл определения внешней StepFunction:
{
"Comment": "A Retry example of the Amazon States Language using an AWS Lambda Function",
"StartAt": "HelloWorld",
"States": {
"HelloWorld": {
"Type": "Task",
"Resource": {"Fn::Sub" : "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${HelloWorldLambdaFunctionName}" },
"End": true
}
}
}
Вот документация по созданию пользовательских ресурсов AWS с лямбда-поддержкой.
Там все еще проблема с этим. Transform/Include преобразует внешние логические свойства шаблона в строковые свойства. Следовательно, DefinitionString
"DefinitionString": {
"States": {
"HelloWorld": {
"Type": "Task",
"Resource": "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${HelloWorldLambdaFunctionName}",
**"End": true**
}
},
"Comment": "A Retry example of the Amazon States Language using an AWS Lambda Function",
"StartAt": "HelloWorld"
}
становится
"DefinitionString": {
"States": {
"HelloWorld": {
"Type": "Task",
"Resource": _realLambdaFunctionArn_,
**"End": "true"**
}
},
"Comment": "A Retry example of the Amazon States Language using an AWS Lambda Function",
"StartAt": "HelloWorld"
}
Затем CloudFormation жалуется, что определение StepFunction недействительно:
Invalid State Machine Definition: 'SCHEMA_VALIDATION_FAILED: Expected value of type Boolean at /States/HelloWorld/End'
Это проблема CloudFormation Transform/Include? Может ли кто-то из AWS сделать заявление по этому поводу?
В то же время, одним из решений может быть использование yaml вместо json для внешнего хранения определения степ-функции. Нет строки, выходящей из:
DefinitionString:
Fn::Sub: |
{
"Comment": "A Retry example of the Amazon States Language using an AWS Lambda Function",
"StartAt": "HelloWorld",
"States": {
"HelloWorld": {
"Type": "Task",
"Resource": "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${HelloWorldLambdaFunctionName}",
"End": true
}
}
}
Здесь вы можете найти другой подход к этой проблеме: https://github.com/aws-samples/lambda-refarch-imagerecognition
И вот краткое резюме:
Как часть вашего файла шаблона CloudFormation (скажем, он называется template.yml), вы определяете конечный автомат следующим образом:
LambdaA:
...
LambdaB:
...
LambdaC:
...
MyStateMachine:
Type: AWS::StepFunctions::StateMachine
Properties:
StateMachineName: !Sub 'MyMachine-${SomeParamIfNeeded}'
DefinitionString:
!Sub
- |-
##{{STATEMACHINE_DEF}}
- {
lambdaArnA: !GetAtt [ LambdaA, Arn ],
lambdaArnB: !GetAtt [ LambdaB, Arn ],
lambdaArnC: !GetAtt [ LambdaC, Arn ]
}
RoleArn: !GetAtt [ StatesExecutionRole, Arn ]
Тогда вам понадобится скрипт замены, здесь вы можете найти один: inject_state_machine_cfn.py
Затем вы можете создать свое определение конечного автомата в виде отдельного файла, скажем, state_machine.json с содержимым:
{
"Comment": "This is an example of Parallel State Machine type",
"StartAt": "Parallel",
"States": {
"Parallel": {
"Type": "Parallel",
"Next": "Final State",
"Branches": [
{
"StartAt": "Run Lambda A",
"States": {
"Run Lambda A": {
"Type": "Task",
"Resource": "${lambdaArnA}",
"ResultPath": "$.results",
"End": true
}
}
},
{
"StartAt": "Run Lambda B",
"States": {
"Run Login Functionality Test": {
"Type": "Task",
"Resource": "${lambdaArnB}",
"ResultPath": "$.results",
"End": true
}
}
}
]
},
"Final State": {
"Type": "Task",
"Resource": "${lambdaArnC}",
"End": true
}
}}
Когда у вас есть все эти элементы, вы можете вызвать функцию преобразования:
python inject_state_machine_cfn.py -s state_machine.json -c template.yml -o template.complete.yml