I can't exactly tell the best practices as those can be different for everyone. We had a similar concern as yours that we don't want the DevOps team applying Terraform changes every time the Dev team(s) wanted to push updates.
Though we create all our infra via Terraform, we made an exception in this case and used a bit of aws ecs cli
.
We ended up creating a tiny pipeline in one of our ops orchestration tools which takes a JSON file as input to
register-task-definition
create/update-service
(depending on if the service exists already or not) &
update-service with --force-new-deployment
(to start a new deployment with no service definition changes in case Devs update the Docker image with the same image/tag combination (my_image:latest ))
The JSON file would contain all info needed to register a task definition or create/update-service. We have 2 JSON files (which we'd eventually merge before applying) and Devs would only be able to change parameters related to their app(s). All other information like sidecar container config
, logConfiguration
, roles
, loadBalancers
, networkConfiguration
etc would be maintained in a separate JSON which Devs wouldn't have access to - just to give us peace of mind that they won't accidentally change something they don't have to, at least themselves.
Just to elaborate further it with an example how 2 JSON files look for a, let's say, task definition
Dev-file:
{
"containerDefinitions": [
{
},
{
"name": "app",
"image": "nginx",
"cpu": 0,
"memory": 0,
"portMappings": [
{
"containerPort": 443,
"hostPort": 443,
"protocol": "tcp"
}
]
}
],
"memory": "0",
"cpu": "0"
}
DevOps-file:
{
"containerDefinitions": [
{
"name": "log_router",
"image": "906394416424.dkr.ecr.us-east-1.amazonaws.com/aws-for-fluent-bit:stable",
"cpu": 0,
"memory": 0,
"firelensConfiguration":{
"type":"fluentbit"
},
"essential": true
},
{
"logConfiguration": {
"logDriver": "awsfirelens",
"options": {
"Name":"http",
"Host":"logstash",
"Port":"8090",
"Format":"json",
"Retry_Limit": "2"
}
}
}
],
"executionRoleArn": "arn:aws:iam::123456789:role/ecsTaskExecutionRole",
"networkMode": "awsvpc",
"requiresCompatibilities": [
"FARGATE"
],
"tags": [
{
"key": "Terraform",
"value": "false"
}
]
}
And just using a simple jq
cmd, we'd merge those and pass it onto the aws ecs cli cmd as --cli-input-json
jq -s 'add + {containerDefinitions: map(.containerDefinitions) | transpose | map(add)}' Dev-file.json DevOps-file.json > actual-file.json
Again, this may not be a solution that suits your needs but just something that works for us. Hope it helps.