UP | HOME

AWS ECS Container Overrides for Events Targets

Table of Contents

Using AWS CloudFormation to configure AWS CloudWatch Events to target ECS is not as well documented as it should be. Here, we walk-through a highly specific use case where the documentation was not here to help, how the solution was found, and possibly some insight when facing similar issues in the future.

In a previous post, I made the fecicious call out to use the source.

When the documentation fails you, use the source.

What to do when there isn't source (code) available?

Problem

While attempting to make some infrastucture changes, I wanted to create a scheduled event to target a specific task. However, I needed to be able to override the command arguments sent to the task for specific instances of the schedule. I started with the Events Rule Cloudformation Documentation. Following from there, I dug into the targets documentation. Thus far, I might have the following JSON for my CloudFormation template:

"ScheduledEvent": {
    "Type": "AWS::Events::Rule",
    "Properties": {
        "Description": "Scheduled Event that happens periodically",
        "Name": {"Fn::Sub": "${Name}-${AWS::Region}-ScheduledEvent"},
        "ScheduleExpression": "rate(15 minutes)",
        "Targets": [
            {
                "Arn": {"Fn::Sub": "arn:aws:ecs:${AWS::Region}:${AWS::AccountId}:cluster/${ECSCluster}"},
                "Id": {"Fn::Sub": "${Name}-Scheduled-Events"},
                "RoleArn": {"Fn::GetAtt": ["SchedulerEventsRole", "Arn"]},
                "EcsParameters": {
                    "TaskCount": 1,
                    "TaskDefinitionArn": {"Ref": "TaskDefinition"},
                }
            }
        ]
    }
}

However, what variable is used to override the command parameters of the task? The documentation has a few keys that might be useful for this prupose. EcsParameters seems the most promising from the beginning but this key is more for informing the Events Rule how to target the ECS Task. RunCommandParameters also seems promissing, however, this is for EC2 targets. Finally, Input seems to be the last available option that seems generic enough to fit the needs.

Valid JSON text passed to the target. If you use this property, nothing from the event text itself is passed to the target.

This is particularly vague. I suppose we can insert some "text" and see what happens.

Exploring the Problem

Otherwising shooting in the dark…

Focusing in on the Input key, I first tried the following:

"Input": "command overrides go here",

However, this failed in CloudFormation with the follwing message:

JSON syntax error in input for target ...

Obivously plain strings are not compatiable JSON.

Let's try an "object" instead:

"Input": "{\"command\": \"command overrides go here\"}",

CloudFormation accepted this construction. However, nothing seemed to work the way expected. At first, I was curious how even the body of the "Input" key was being passed along to the task. From the task side, printing the standard input and the arguments yielded nothing. Examining the target from the Events console didn't seem to shed any light on the issue, everything looked fine. Of note, however, is that there was no mention of the "Input" or override variables available from this screen.

Next, I looked into the ECS console examining the scheduled tasks for the cluster, I could see the task. Examinging the task showed an error stating the "command" key was invalid in this context.

Finally, I gave up and used the console to edit the target and the "Input" key. Doing so yielded the JSON structure I was needing to override for the task at hand.

{
    "containerOverrides": [
        {
            "name": "container name",
            "command": [
                "command overrides go here"
            ]
        }
    ]
}

Solution

However, the "Input" key is a string encoded JSON structure, so the full solution is the following snippet:

"ScheduledEvent": {
    "Type": "AWS::Events::Rule",
    "Properties": {
        "Description": "Scheduled Event that happens periodically",
        "Name": {"Fn::Sub": "${Name}-${AWS::Region}-ScheduledEvent"},
        "ScheduleExpression": "rate(15 minutes)",
        "Targets": [
            {
                "Arn": {"Fn::Sub": "arn:aws:ecs:${AWS::Region}:${AWS::AccountId}:cluster/${ECSCluster}"},
                "Id": {"Fn::Sub": "${Name}-Scheduled-Events"},
                "Input": "{\"containerOverrides\": [{\"name\": \"container name\", \"command\": [\"command overrides go here!\"]}]}",
                "RoleArn": {"Fn::GetAtt": ["SchedulerEventsRole", "Arn"]},
                "EcsParameters": {
                    "TaskCount": 1,
                    "TaskDefinitionArn": {"Ref": "TaskDefinition"},
                }
            }
        ]
    }
}

This structure may seem familiar. This structure is documented in the AWS API documentation, which is referenced in the ECS RunTask API documentation.

Conclusion

The connection between "Input" is a JSON encoded string and pass the "containerOverrides" structure is missing or not obvious at best. Hopefully, this simple example helps guide others to the right solution. If nothing else, it will remind me that when a variable is particularly vague about its usage, it might help to examine the AWS API documentation more closely.