Разверните 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
Другие вопросы по тегам