问题
I'm trying to create a scheduled task (CloudWatch Events Rule) in my CloudFormation Template that would have the following EcsParameters:
EcsParameters:
LaunchType: FARGATE
NetworkConfiguration:
AwsVpcConfiguration:
AssignPublicIp: !Ref PublicIpAssignment
SecurityGroups:
- !Ref EcsSecurityGroups
Subnets:
- !Ref SubnetName
TaskCount: 1
TaskDefinitionArn: !Ref TaskDefinitionOne
My ECS CLuster is launched on Fargate and not EC2, and I do NOT have a service running (use case doesn't need a long running process, directly scheduling tasks from events rules.)
Whenever I run this template (with LaunchType
and NetworkConfiguration
) the stack creation fails, with this error:
Encountered unsupported property NetworkConfiguration
As an alternative, I also tried launching the scheduled task from AWS CLI, but it seems like the network config and launch type options are not available there either:
Parameter validation failed: Unknown parameter in Targets[0].EcsParameters: "LaunchType", must be one of: TaskDefinitionArn, TaskCount
According to this page on the AWS Documentation itself, I should be able to specify LaunchType
and NetworkConfiguration
in my EcsParameters
section in Targets
in Properties
of the AWS::Events::Rule
resource.
Is there anything I can try that might work?
回答1:
CloudFormation has not yet caught up with the parameters needed to run a Fargate task as the direct target of a CloudWatch Events Rule. In the meantime, you can achieve the same result by having the rule target a Lambda function which runs the Fargate task.
For this to work the Events Rule will need lambda:InvokeFunction
permission on the Lambda function, and the Lambda function will need the ecs:RunTask
and iam:PassRole
permission on the appropriate resources (in addition to the usual logs permissions in AWSLambdaBasicExecutionRole).
Edit: Here is an example CF template that shows what I'm talking about. (It's pieced together and simplified from what we're using, so not tested, but hopefully illustrates the process.)
Parameters:
#ClusterName
#Subnets
#SecurityGroups
#CronExpression
#TaskDefinitionArn
#TaskRoleArn
#ExecutionRoleArn
Resources:
FargateLauncherRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub ${AWS::StackName}-FargateLauncher-${AWS::Region}
AssumeRolePolicyDocument:
Statement:
-
Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Path: /
FargateLauncherPolicy:
Type: AWS::IAM::Policy
Properties:
PolicyName: !Sub ${AWS::StackName}-FargateLauncher-${AWS::Region}
PolicyDocument:
Version: 2012-10-17
Statement:
-
Sid: RunTaskAccess
Effect: Allow
Action:
- ecs:RunTask
Resource: '*'
-
Sid: PassRoleAccess
Effect: Allow
Action:
- iam:PassRole
Resource:
# whatever you have defined in your TaskDefinition, if any
- !Ref TaskRoleArn
- !Ref ExecutionRoleArn
Roles:
- !Ref FargateLauncherRole
FargateLauncher:
Type: AWS::Lambda::Function
DependsOn: FargateLauncherPolicy
Properties:
Environment:
Variables:
CLUSTER_NAME: !Ref ClusterName
SUBNETS: !Ref Subnets
SECURITY_GROUPS: !Ref SecurityGroups
Handler: index.handler
Role: !GetAtt FargateLauncherRole.Arn
Runtime: python3.6
Code:
ZipFile: |
from os import getenv
from boto3 import client
ecs = client('ecs')
def handler(event, context):
ecs.run_task(
cluster=getenv('CLUSTER_NAME'),
launchType='FARGATE',
taskDefinition=event.get('taskDefinition'),
count=1,
platformVersion='LATEST',
networkConfiguration={'awsvpcConfiguration': {
'subnets': getenv('SUBNETS').split(','),
'securityGroups': getenv('SECURITY_GROUPS').split(','),
'assignPublicIp': 'DISABLED'
}})
Schedule:
Type: AWS::Events::Rule
Properties:
ScheduleExpression: !Sub "cron(${CronExpression})"
State: ENABLED
Targets:
-
Id: fargate-launcher
Arn: !GetAtt FargateLauncher.Arn
Input: !Sub |
{
"taskDefinition": "${TaskDefinitionArn}"
}
InvokePermission:
Type: AWS::Lambda::Permission
Properties:
FunctionName: !Ref FargateLauncher
Action: lambda:InvokeFunction
Principal: events.amazonaws.com
SourceArn: !GetAtt Schedule.Arn
I define the Lambda function in my cluster stack, where I already have ClusterName
, Subnets
, and SecurityGroups
parameters, and can pass them directly to the Lambda environment. The schedule and invoke permission can then be defined in one or many separate stacks, passing in the TaskDefinition
for each task via the input to the Lambda function. That way you can have one Lambda per cluster but use as many different tasks as needed. You can also add a custom command string and/or other container overrides to the Lambda input which can be passed on via the overrides
param of run_task
.
Edit #2: Here's an example Fargate TaskDefinition that can go in a CF template:
TaskDefinition:
Type: AWS::ECS::TaskDefinition
Properties:
Family: !Ref Family
Cpu: !Ref Cpu
Memory: !Ref Memory
NetworkMode: awsvpc
ExecutionRoleArn: !Ref ExecutionRoleArn
TaskRoleArn: !Ref TaskRoleArn
RequiresCompatibilities:
- FARGATE
ContainerDefinitions:
- Name: !Ref ContainerName
Essential: true
Image: !Ref Image
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-group: !Ref LogGroup
awslogs-region: !Ref AWS::Region
awslogs-stream-prefix: !Ref LogPrefix
回答2:
Even though AWS hasn't updated the documentation by today (Jul.15, 2019) it's working as described by the initial poster.
回答3:
After a day of research, it looks like AWS still hasn't released support for this though CloudFormation.
However, here is an alternative that did work through the aws events put-targets
command on the cli.
This method fails for older versions of the cli. run this to update: pip install awscli --upgrade --user
This is the version i am on now: aws-cli/1.16.9 Python/2.7.15 Darwin/17.7.0 botocore/1.11.9
Use the aws events put-targets --rule <value> --targets <value>
command. Make sure that you have a rule already defined on your cluster. If not, you can do that with the aws events put-rule
cmd. Refer to the AWS docs for put-rule, and for put-targets.
An example of a rule from the documentation is given below:
aws events put-rule --name "DailyLambdaFunction" --schedule-expression "cron(0 9 * * ? *)"
The put-targets command that worked for me is this:
aws events put-targets --rule cli-RS-rule --targets '{"Arn": "arn:aws:ecs:1234/cluster/clustername","EcsParameters": {"LaunchType": "FARGATE","NetworkConfiguration": {"awsvpcConfiguration": {"AssignPublicIp": "ENABLED", "SecurityGroups": [ "sg-id1233" ], "Subnets": [ "subnet-1234" ] }},"TaskCount": 1,"TaskDefinitionArn": "arn:aws:ecs:1234:task-definition/taskdef"},"Id": "sampleID111","RoleArn": "arn:aws:iam:1234:role/eventrole"}'
来源:https://stackoverflow.com/questions/52208700/creating-a-target-for-a-cloudwatch-event-rule-via-cloudformation-for-a-fargate