AWS CDKで作成したAWS Step Functions State MachineのASL定義をjqコマンドで取得する

2022.07.24

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは、CX事業本部 IoT事業部の若槻です。

AWS Step Functions Localを使いたい場合、テスト対象のState MachineのASL(Amazon States Language)定義を取得する必要があります。

そこで今回は、AWS CDKでAWS Step Functionsの開発を行っている場合に、State MachineのASL定義をjqコマンドで取得する方法を確認してみました。

結論

次のコマンドを実行すれば取得できます。

$ stackName=<stackName>
$ stateMachineName=<stateMachineName>
$ jq --arg stateMachineName "$stateMachineName" \
  '.Resources[] | select(.Properties.StateMachineName == $stateMachineName)' \
  cdk.out/${stackName}.template.json | \
  jq -r .Properties.DefinitionString | jq .

やってみた

CDK SynthやCDK DeployによるBuildを行うと、StackのCloudFormation Templateがcdk.out/<stackName>.template.jsonに作成されます。

例えば次のStackの場合。

lib/aws-app-stack.ts

import { Construct } from 'constructs';
import { aws_stepfunctions, Stack, StackProps } from 'aws-cdk-lib';

export class AwsAppStack extends Stack {
  constructor(scope: Construct, id: string, props: StackProps) {
    super(scope, id, props);

    // StateMachine1
    new aws_stepfunctions.StateMachine(this, 'stateMachine1', {
      stateMachineName: 'stateMachine1',
      definition: new aws_stepfunctions.Pass(this, 'pass1'),
    });

    // StateMachine2
    new aws_stepfunctions.StateMachine(this, 'stateMachine2', {
      stateMachineName: 'stateMachine2',
      definition: new aws_stepfunctions.Map(this, 'Map State', {
        itemsPath: aws_stepfunctions.JsonPath.stringAt('$.inputForMap'),
      }).iterator(new aws_stepfunctions.Pass(this, 'pass2')),
    });
  }
}

次のようなTemplate Fileが生成されます。

cdk.out/AwsAppStack.template.json

{
 "Resources": {
  "stateMachine1Role7BF1589E": {
   "Type": "AWS::IAM::Role",
   "Properties": {
    "AssumeRolePolicyDocument": {
     "Statement": [
      {
       "Action": "sts:AssumeRole",
       "Effect": "Allow",
       "Principal": {
        "Service": "states.ap-northeast-1.amazonaws.com"
       }
      }
     ],
     "Version": "2012-10-17"
    }
   },
   "Metadata": {
    "aws:cdk:path": "AwsAppStack/stateMachine1/Role/Resource"
   }
  },
  "stateMachine174B105B6": {
   "Type": "AWS::StepFunctions::StateMachine",
   "Properties": {
    "RoleArn": {
     "Fn::GetAtt": [
      "stateMachine1Role7BF1589E",
      "Arn"
     ]
    },
    "DefinitionString": "{\"StartAt\":\"pass1\",\"States\":{\"pass1\":{\"Type\":\"Pass\",\"End\":true}}}",
    "StateMachineName": "stateMachine1"
   },
   "DependsOn": [
    "stateMachine1Role7BF1589E"
   ],
   "Metadata": {
    "aws:cdk:path": "AwsAppStack/stateMachine1/Resource"
   }
  },
  "stateMachine2Role1C82D998": {
   "Type": "AWS::IAM::Role",
   "Properties": {
    "AssumeRolePolicyDocument": {
     "Statement": [
      {
       "Action": "sts:AssumeRole",
       "Effect": "Allow",
       "Principal": {
        "Service": "states.ap-northeast-1.amazonaws.com"
       }
      }
     ],
     "Version": "2012-10-17"
    }
   },
   "Metadata": {
    "aws:cdk:path": "AwsAppStack/stateMachine2/Role/Resource"
   }
  },
  "stateMachine23B5C198F": {
   "Type": "AWS::StepFunctions::StateMachine",
   "Properties": {
    "RoleArn": {
     "Fn::GetAtt": [
      "stateMachine2Role1C82D998",
      "Arn"
     ]
    },
    "DefinitionString": "{\"StartAt\":\"Map State\",\"States\":{\"Map State\":{\"Type\":\"Map\",\"End\":true,\"Iterator\":{\"StartAt\":\"pass2\",\"States\":{\"pass2\":{\"Type\":\"Pass\",\"End\":true}}},\"ItemsPath\":\"$.inputForMap\"}}}",
    "StateMachineName": "stateMachine2"
   },
   "DependsOn": [
    "stateMachine2Role1C82D998"
   ],
   "Metadata": {
    "aws:cdk:path": "AwsAppStack/stateMachine2/Resource"
   }
  },
  "CDKMetadata": {
   "Type": "AWS::CDK::Metadata",
   "Properties": {
    "Analytics": "v2:deflate64:H4sIAAAAAAAA/1WJzQrCMBCEn6X3ZLU9CJ49F6Q+gKzpFrc/m9JN9BDy7sZ6Ega+mfkaaM5QV/hW6/rJzvyAdAvoJlOue9JA6xDFBfaikK6oaooO1KJ7spC5DPK3W1yzYVwgdX7e9Zc5743Ux81RNuJ7glEPr/oEJcdqVGa7RQm8EHQ/fgC550rrmwAAAA=="
   },
   "Metadata": {
    "aws:cdk:path": "AwsAppStack/CDKMetadata/Default"
   }
  }
 },
 "Parameters": {
  "BootstrapVersion": {
   "Type": "AWS::SSM::Parameter::Value<String>",
   "Default": "/cdk-bootstrap/hnb659fds/version",
   "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]"
  }
 },
 "Rules": {
  "CheckBootstrapVersion": {
   "Assertions": [
    {
     "Assert": {
      "Fn::Not": [
       {
        "Fn::Contains": [
         [
          "1",
          "2",
          "3",
          "4",
          "5"
         ],
         {
          "Ref": "BootstrapVersion"
         }
        ]
       }
      ]
     },
     "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI."
    }
   ]
  }
 }
}

このうちState MachineのASL定義はハイライト部分(DefinitionString)となります。そして、Templateにおける各Resourceは、Logical IDをKeyとしたオブジェクトとなり、その配下にStateMachineNameとDefinitionStringがあります。

よって、指定の名前のStateMachineのリソースのオブジェクトを取得したい場合は、jqコマンドを次のように実行します。

stateMachine1のオブジェクトを取得

$ stackName=AwsAppStack
$ stateMachineName=stateMachine1
$ jq --arg stateMachineName "$stateMachineName" \
  '.Resources[] | select(.Properties.StateMachineName == $stateMachineName)' \
  cdk.out/${stackName}.template.json
{
  "Type": "AWS::StepFunctions::StateMachine",
  "Properties": {
    "RoleArn": {
      "Fn::GetAtt": [
        "stateMachine1Role7BF1589E",
        "Arn"
      ]
    },
    "DefinitionString": "{\"StartAt\":\"pass1\",\"States\":{\"pass1\":{\"Type\":\"Pass\",\"End\":true}}}",
    "StateMachineName": "stateMachine1"
  },
  "DependsOn": [
    "stateMachine1Role7BF1589E"
  ],
  "Metadata": {
    "aws:cdk:path": "AwsAppStack/stateMachine1/Resource"
  }
}

さらにASLのみを抽出したい場合は、さらにjqコマンドへのパイプを行います。

stateMachine1のASLを取得

$ stackName=AwsAppStack
$ stateMachineName=stateMachine1
$ jq --arg stateMachineName "$stateMachineName" \
  '.Resources[] | select(.Properties.StateMachineName == $stateMachineName)' \
  cdk.out/${stackName}.template.json | \
  jq -r .Properties.DefinitionString | jq .
{
  "StartAt": "pass1",
  "States": {
    "pass1": {
      "Type": "Pass",
      "End": true
    }
  }
}

もう一つのStateMachineのASLも同様に取得できます。

stateMachine2のASLを取得

$ stackName=AwsAppStack
$ stateMachineName=stateMachine2
$ jq --arg stateMachineName "$stateMachineName" \
  '.Resources[] | select(.Properties.StateMachineName == $stateMachineName)' \
  cdk.out/${stackName}.template.json | \
  jq -r .Properties.DefinitionString | jq .
{
  "StartAt": "Map State",
  "States": {
    "Map State": {
      "Type": "Map",
      "End": true,
      "Iterator": {
        "StartAt": "pass2",
        "States": {
          "pass2": {
            "Type": "Pass",
            "End": true
          }
        }
      },
      "ItemsPath": "$.inputForMap"
    }
  }
}

おわりに

AWS CDKで作成したAWS Step Functions State MachineのASL定義をjqコマンドで取得する方法を確認してみました。

AWS CLIのstepfunctions describe-state-machineコマンドを使ってもASLを取得できなくは無いのでしょうが、AWS CDKの開発環境であればローカルにBuildがされるのであれば、せっかくならそっちから取得してしまえということでjqを使ってみました。

以上