LoginSignup
31
2
この記事誰得? 私しか得しないニッチな技術で記事投稿!

既存のECSタスク定義を参照するStepFunctionsをCDKで作るメモ

Posted at

概要

  • ECSタスクを起動するStepFunctionsのステートマシンを作りたい
    • ステートマシンは任意のコマンドをECSタスクに渡せるものとする
  • CDKでステートマシンを作成する際に、ECSタスクを同時に作成するか既に作成しているかで実装コードが違う

作りたいもの

StepFunctionsのステートマシンからECSタスクを起動したい。
ここではCDKを使って構築する。

ECSタスク定義もCDKで生成した場合

以下コードのECSタスク関連のプロパティは別途CDKで生成したリソースのインスタンスを設定しているとする。

ステートマシンのIAMロールとポリシーは自動で作られるのでコードを書く必要はない。

// コンテナの実行コマンド上書きに使う、taskDefinitionはリソースをCDKで作成済み
const containerDefinition = taskDefinition.findContainer('hoge')
if (!containerDefinition) {
  throw new Error(`Not Found container hoge`)
}

// ステートマシンのECSタスクを実行するステート
const state = new tasks.EcsRunTask(this, 'hogefuga-runFargate', {
  integrationPattern: sfn.IntegrationPattern.RUN_JOB,
  cluster,  // リソースをCDKで作成済み
  taskDefinition,  // リソースをCDKで作成済み
  securityGroups,  // リソースをCDKで作成済み
  containerOverrides: [  // 入力値によりコンテナの実行コマンドを切り替えている
    {
      containerDefinition,
      command: sfn.JsonPath.listAt('$.command')
    }
  ],
  launchTarget: new tasks.EcsFargateLaunchTarget()
})

const sf = new sfn.StateMachine(this, 'hogefuga-StateMachine', {
  definition: state
})

生成したステートマシンのIAMロールには以下のポリシーが付与される。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": "ecs:RunTask",
            "Resource": "arn:aws:ecs:ap-northeast-1:XXXXXXXXXXXX:task-definition/hogefuga",
            "Effect": "Allow"
        },
        {
            "Action": [
                "ecs:StopTask",
                "ecs:DescribeTasks"
            ],
            "Resource": "*",
            "Effect": "Allow"
        },
        {
            "Action": "iam:PassRole",
            "Resource": [
                "arn:aws:iam::XXXXXXXXXXXX:role/hogefuga-taskRole",
                "arn:aws:iam::XXXXXXXXXXXX:role/hogefuga-executionRole"
            ],
            "Effect": "Allow"
        },
        {
            "Action": [
                "events:PutTargets",
                "events:PutRule",
                "events:DescribeRule"
            ],
            "Resource": "arn:aws:events:ap-northeast-1:XXXXXXXXXXXX:rule/StepFunctionsGetEventsForECSTaskRule",
            "Effect": "Allow"
        }
    ]
}

余談

StateMachinePropsの definition はaws-cdkの v2.85.0 からdeprecatedになった。
新しいプロパティ definitionBody により文字列とファイルでステートマシンの定義を設定できるようになった。
今までの State (上のコードの new tasks.EcsRunTask() )を使う場合は definitionBody: sfn.DefinitionBody.fromChainable(state) とすればそのまま使える。

また、文字列とファイルによる定義の場合は自動的にIAMロールにポリシーが付与されないらしい。

ECSタスク定義はCDKで生成していない場合

以前から運用しているシステムではECSタスク定義をCDK管理していないケースがままある。
こういった場合はクラスターやタスク定義は既存のリソースから読み込む作業が必要になる。

ITaskDefinitionが使用できない

タスク定義を既存のリソースから読み込むのに使用する ecs.TaskDefinition.fromTaskDefinitionArn() などの型は ITaskDefinition になる。

今回のコードではこのインターフェースにないプロパティやメソッドを必要とするのでエラーになってしまう。
先ほどのコードのタスク定義のみ fromTaskDefinitionArn() を使ったものに変更したコードが以下のもの。

// taskDefinitionArnからタスク定義を読み込む
const iTaskDefinition = ecs.TaskDefinition.fromTaskDefinitionArn(this, 'hoge-taskDefinition-arn', `${taskDefinition.taskDefinitionArn}`)

// コンテナの実行コマンド上書きに使う
const containerDefinition = iTaskDefinition.findContainer('hoge')
if (!containerDefinition) {
  throw new Error(`Not Found container hoge`)
}

// ステートマシンのECSタスクを実行するステート
const ecsRunTask = new tasks.EcsRunTask(this, 'hogefuga-runFargate', {
  integrationPattern: sfn.IntegrationPattern.RUN_JOB,
  cluster: ecscluster,
  taskDefinition: iTaskDefinition,
  securityGroups,
  containerOverrides: [  // 入力値によりコンテナの実行コマンドを切り替えている
    {
      containerDefinition,
      command: sfn.JsonPath.listAt('$.command')
    }
  ],
  launchTarget: new tasks.EcsFargateLaunchTarget()
})

このコードをcdk diffすると、 findContainer() で以下のようなエラーが発生する。

Type 'ITaskDefinition' is missing the following properties from type 'TaskDefinition': family, containers, volumes, placementConstraints, and 32 more.

findContainer() をコメントアウトした場合でも、 new tasks.EcsRunTask() で同様のエラーになるのだが、こちらのドキュメントには以下のように書かれている。

Note: this must be TaskDefinition, and not ITaskDefinition, as it requires properties that are not known for imported task definitions If you want to run a RunTask with an imported task definition, consider using CustomState

つまり、既存のECSタスク定義を参照するStepFunctionsのステートマシンではCustomStateを使う必要がある。

大抵のConstruct Propsはインターフェースのプロパティとメソッドを使っているが、これはその例外らしい。

CustomStateを使う

CustomStateを使うと Amazon States Language (ASL) を使った形式でステートマシンを記述できる。
要はAWSコンソールでステートマシンの定義にアクセスしたときに表示される形式で記述する。

CustomStateを使ったコードは以下の通り。
クラスターでもタスク定義でもARNはインターフェースから取得できる、サブネットやセキュリティグループも同様。

const stateJson = {
  Type: 'Task',
  Resource: 'arn:aws:states:::ecs:runTask.sync',
  Parameters: {
    Cluster: 'arn:aws:ecs:ap-northeast-1:XXXXXXXXXXXX:cluster/hogefuga-cluster',
    TaskDefinition: 'arn:aws:ecs:ap-northeast-1:XXXXXXXXXXXX:task-definition/hogefuga',
    NetworkConfiguration: {
      AwsvpcConfiguration: {
        Subnets: Subnets.map((r) => r.subnetId),
        SecurityGroups: SecurityGroups.map((r) => r.securityGroupId)
      }
    },
    Overrides: {
      ContainerOverrides: [
        {
          Name: 'hoge',
          'Command.$': '$.command'
        }
      ]
    },
    LaunchType: 'FARGATE'
  }
}

const state = new sfn.CustomState(this, 'hogefuga-runFargate', {
  stateJson,
  // role を設定しないとステートマシンのIAMロールにポリシーが付与されない
})

付与すべきIAMポリシーについて

CustomStateであっても必要なIAMポリシーはtasks.EcsRunTaskを使った場合のポリシー一覧と同じになる。

今回作成しているStandardのステートマシンではステートが終了するまで処理を待つことができ、そのためにはEventBridgeを使用しているらしい。
そしてその為には以下の部分が必要になる。

{
    "Action": [
        "events:PutTargets",
        "events:PutRule",
        "events:DescribeRule"
    ],
    "Resource": "arn:aws:events:ap-northeast-1:XXXXXXXXXXXX:rule/StepFunctionsGetEventsForECSTaskRule",
    "Effect": "Allow"
}

rule/StepFunctionsGetEventsForECSTaskRule の部分はターゲットのリソースによって異なる。
例えば AWS Batch をターゲットにする場合は rule/StepFunctionsGetEventsForBatchJobsRule にする必要がある。

どのポリシーを設定すべきかは、上のドキュメントの IAM ロールの作成に使用されるポリシーテンプレートトピック のリンク先で確認できる。

ここの設定を間違えると以下のようなエラーが発生することがある。

Resource handler returned message: "'arn:aws:iam::XXXXXXXXXX:role/hogefuga-role' is not a
uthorized to create managed-rule.
(Service: AWSStepFunctions; Status Code: 400; Error Code: AccessDeniedException; 
Request ID: XXXXXXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX; 
Proxy: null)" 
(RequestToken: XXXXXXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX, HandlerErrorCode: AccessDenied)

まとめ

StepFunctionsでECSタスクを呼び出す場合、インターフェースが使えないのでターゲットのECSタスク定義のCDK管理状態によって対応する方法が大きく異なってしまう。

31
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
31
2