11
votes

Long story short, I don't want to have to hardcode in ECS task definition revision numbers for tasks into my lambda source codes. It's essentially toil updating my source code each time I have an updated task definition. In the boto3 documentation for ECS run_task(), it clearly states

taskDefinition (string) -- [REQUIRED]

The family and revision (family:revision ) or full ARN of the task definition to run. If a revision is not specified, the latest ACTIVE revision is used.

However, I am finding that if I define the taskDefinition parameter in client.run_task() with no specific revision number, I get a permission error:

An error occurred (AccessDeniedException) when calling the RunTask operation: User: arn:aws:sts::MY_ACCOUNT_ID:assumed-role/my-lambda-role/trigger-ecs-task is not authorized to perform: ecs:RunTask on resource: arn:aws:ecs:MY_REGION:MY_ACCOUNT_ID:task-definition/an-important-task

If I switch my definition to an-important-task:LATEST or an-important-task:*, I get another error:

...is not authorized to perform: ecs:RunTask on resource: *

This is strange, because it appears contrary to what the documentation states- when I include a revision number, like an-important-task:5, the lambda triggers perfectly. In my lambda function, I simply invoke my ECS task:

def lambda_handler(event, context):
    client = boto3.client('ecs')
    print("Running task.")
    response = client.run_task(
        cluster='my-cluster', 
        launchType='FARGATE',
        taskDefinition='an-important-task',  # <-- notice no revision number
        count=1,
        platformVersion='LATEST',
        networkConfiguration={
            'awsvpcConfiguration': {
                'subnets': [
                    'subnet-1',
                    'subnet-2'
                ],
                'assignPublicIp': 'DISABLED'
            }
        })
    print("Finished invoking task.")

In my Terraform definition, I've attached the necessary policies to my role:

resource "aws_lambda_function" "trigger-ecs-task" {
  function_name = "trigger-ecs-task"
  handler = "my-lambda-function.lambda_handler"
  role = "${aws_iam_role.lambda.arn}"
  runtime = "python3.6"
  # other stuff related to how I store my source code for the lambda
}

My role definition, and attaching the permissions to run ECS tasks:

resource "aws_iam_role" "lambda" {
  name = "my-lambda-ecs-role"
  assume_role_policy = "${data.aws_iam_policy_document.lambda-assume-role.json}"
}

data "aws_iam_policy_document" "lambda-assume-role" {
  statement {
    actions = [
      "sts:AssumeRole"]

    principals {
      type = "Service"
      identifiers = [
        "lambda.amazonaws.com"]
    }
  }
}

resource "aws_iam_policy" "run-ecs-policy" {
  name = "run-ecs-task-policy"
  policy = <<EOF
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": "ecs:RunTask",
            "Resource": "arn:aws:ecs:MY_REGION:MY_ACCOUNT_ID:task-definition/an-important-task:*"
        },
        {
            "Sid": "Stmt1512361993201",
            "Effect": "Allow",
            "Action": [
                "iam:PassRole"
            ],
            "Resource": [
                "arn:aws:iam::************:role/${data.aws_iam_role.ecs-task-execution-role.name}"
            ]
        }
    ]
}
EOF
}

I attach the policy below:

resource "aws_iam_role_policy_attachment" "service-role-attach" {
  role = "${aws_iam_role.lambda.name}"
  policy_arn = "${aws_iam_policy.run-ecs-policy.arn}"
}

Why is AWS refusing to run my task if I don't specify a specific revision? In my policy definition, I am clearly giving permission to runTask on all revisions of the resource:

arn:aws:ecs:MY_REGION:MY_ACCOUNT_ID:task-definition/an-important-task:*
1
Can you try setting Resource: "*" for the iam:PassRole policy?Milan Cermak
I get the same result. Also, is this only to debug something you think might be causing it? Why would I want to set it to * anyways? Isn't that violating the principle of least permissions?Yu Chen
Yes it is, but if it did work, at least you'd know where the issue is. Too bad it didn't.Milan Cermak
did you try with client.run_task() with latest ACTIVE as revision? any progresses?BMW
@BMW I tried running with an-important-task:LATEST. You mean try w/ an-important-task:ACTIVE?Yu Chen

1 Answers

8
votes

I was able to replicate your behavior and resolved the issue with following Resource in IAM Policy.

{
    "Sid": "VisualEditor1",
    "Effect": "Allow",
    "Action": "ecs:RunTask",
    "Resource": "arn:aws:ecs:MY_REGION:MY_ACCOUNT_ID:task-definition/an-important-task"
}

If you are planning to provide with revision then the resource should have :* in it or it has to exactly exactly match with taskDefinition.

Let me know how it goes at your end!!.