Consider the simple case where you have an JSON file containing an ECS task definition within a repository’s master branch:
https://github.com/someuser/my-ecs-repo/aws/ecs_task_definition.json
{
"family": "production_metrics_app",
"containerDefinitions": [
{
"name": "metrics_runner",
"image": "123456789012.dkr.ecr.us-east-1.amazonaws.com/metrics-checker:latest",
"memory": 300,
"cpu": 10,
"essential": true
}
],
"taskRoleArn": "arn:aws:iam::123456789012:role/metrics_app_iam_role",
"executionRoleArn": "arn:aws:iam::123456789012:role/metrics_app_execution_role"
}
Let’s assume that the master branch is where all the application’s Production code resides. It’s clear from the definition that the associated AWS Account is 123456789012.
Currently, the associated workflow which runs the deployment looks like:
name: Deploy ECS Task
on: [push]
env:
ECR_REPOSITORY: metrics-checker
ECS_CLUSTER: production-ecs-cluster
ECS_TASK_DEFINITION: my-ecs-repo/aws/ecs_task_definition.json
permissions:
contents: read
id-token: write
jobs:
deploy_prod:
name: ECS Prod deployment
runs-on: ubuntu-latest
steps:
- name: "AWS Credentials"
uses: aws-actions/configure-aws-credentials@v3
with:
...
...
...
- name: "Deploy task to ECS"
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
with:
task-definition: ${{ env.ECS_TASK_DEFINITION }}
cluster: ${{ env.ECS_CLUSTER }}
To look at how templating can benefit productivity and code reusability, we’ll tweak the workflow and JSON file to also deploy the task to a secondary AWS account 987647837647, used primarily for user acceptance testing (UAT). Code for this environment is stored in branch: uat.
Our requirement for the build is:
- Depending on branch being pushed to (master or uat) a job should run to render the template with the correct variables.
To use Jinja templating in our workflow, we can leverage from existing GitHub jinja2-action.
Convert ECS Task JSON to Template
For now, we will only concentrate templating the following fields:
- target environment: resolves to either “production” or “uat“
- target AWS account: resolves to either “123456789012” or “987647837647“
Update the file contents, replacing hard-coded values with environment variable “look-up” equivalents—refer to GitHub action cuchi/jinja2-action@v1.2.2 standards for using variables:
{
"family": "{{ env.get('env_target') }}_metrics_app",
"containerDefinitions": [
{
"name": "metrics_runner",
"image": "{{ env.get('target_aws_acct') }}.dkr.ecr.us-east-1.amazonaws.com/metrics-checker:latest",
"memory": 300,
"cpu": 10,
"essential": true
}
],
"taskRoleArn": "arn:aws:iam::{{ env.get('target_aws_acct') }}:role/metrics_app_iam_role",
"executionRoleArn": "arn:aws:iam::{{ env.get('target_aws_acct') }}:role/metrics_app_execution_role"
}
Taking target environment as an example, in the above template, the expression:
"{{ env.get('env_target') }}_metrics_app"
would resolve to:
"production_metrics_app"
Updated Workflow
Our revised basic workflow is shown below.
name: Deploy ECS Task
on:
push:
branches:
- master
- uat
env:
ECR_REPOSITORY: metrics-checker
ECS_TASK_TEMPLATE: my-ecs-repo/aws/ecs_task_template.json
ECS_TASK_RENDERED: my-ecs-repo/aws/ecs_task_rendered.json
target_env: ${{ github.ref == 'refs/heads/master' && 'production' || 'uat' }}
aws_acct_id: ${{ github.ref == 'refs/heads/master' && '123456789012' || '987647837647' }}
permissions:
contents: read
id-token: write
jobs:
deploy_ecs_task:
name: ECS Prod deployment
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- id: jinja
uses: cuchi/jinja2-action@v1.2.2
with:
template: ${{ env.ECS_TASK_TEMPLATE }}
output_file: ${{ env.ECS_TASK_RENDERED }}
data_format: json
strict: true
env:
target_aws_acct: ${{ env.aws_acct_id }}
env_target: ${{ env.target_env }}
- run: |
cat ${{ env.ECS_TASK_RENDERED }}
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v3
with:
aws-region: us-east-1
role-to-assume: arn:aws:iam::${{ env.aws_acct_id }}:role/${{ env.target_env }}_role
role-session-name: samplerolesession
- name: "Deploy task to ECS"
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
with:
task-definition: ${{ env.ECS_TASK_RENDERED }}
cluster: ${{ env.target_env }}-ecs-cluster