Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions ecs-update-and-deploy-task-definition/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,72 @@ permissions:
}
}
```

## How to use

Here is an example deploying two containers in one task definition.

```yaml

ecs-deploy:
- runs-on: ubuntu-latest
name: Deploy ECS task definition
environment: pirates-dev-app-too-tikki-ecr
permissions:
id-token: write # For the GitHub's OIDC Token endpoint

steps:

- name: Set images to deploy ⚙️
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
id: set-images
with:
script: |
// Images data structure documentation:
// https://github.com/oslokommune/composite-actions/blob/main/ecs-update-and-deploy-task-definition/README.md
const images = {};

const initImageBuildResult = "${{ needs.docker-build-push-init.result }}";
const appImageBuildResult = "${{ needs.docker-build-push.result }}";

console.log("Step result for init container image:", initImageBuildResult);
console.log("Step result for app container image:", appImageBuildResult);

if (initImageBuildResult === 'success') {
images["init-container"] = {
"imageRepository": "pirates-dev-too-tikki-init",
"imageDigest": "${{ needs.docker-build-push-init.outputs.image_digest }}",
"imageTag": "${{ needs.docker-build-push-init.outputs.image_version }}"
};
}

if (appImageBuildResult === 'success') {
images["too-tikki"] = {
"imageRepository": "pirates-dev-too-tikki",
"imageDigest": "${{ needs.docker-build-push.outputs.image_digest }}",
"imageTag": "${{ needs.docker-build-push.outputs.image_version }}"
};
}

console.log("Images to deploy:");
console.log(images);

return images;


- name: "Update and deploy ECS task definition with new image URI"
uses: oslokommune/composite-actions/ecs-update-and-deploy-task-definition@... # set digest
with:
aws-region: "eu-west-1"
aws-role-arn: "${{ secrets.AWS_ROLE_ARN }}"

cluster-name: "pirates-dev"
service-name: "too-tikki"
task-definition-name: "too-tikki"

deploy: "true"
wait-for-service-stability: "false"

images: ${{ steps.set-images.outputs.result }}
images-ssm-parameter-name: "/pirates-dev/ecs/too-tikki/images"
```
99 changes: 78 additions & 21 deletions ecs-update-and-deploy-task-definition/action.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
name: "Update and deploy ECS task definition"

description: "Downloads an existing ECS task definition, updates multiple container image URIs, and optionally deploys the updated task definition to the specified ECS service."
description: |
Downloads an existing ECS task definition, updates multiple container image URIs, optionally deploys the updated task
definition to the specified ECS service, and optionally writes the images deployed to an SSM parameter.

inputs:
aws-region:
Expand Down Expand Up @@ -36,17 +38,18 @@ inputs:
required: false
default: "true"


outputs:
task-definition-file-name:
description: "The path to the rendered task definition file."
value: "${{ steps.update-task-definition.outputs.task-definition }}"
images-ssm-parameter-name:
description: "Set this to store images as an SSM parameter. If empty, the action will not store images in SSM."
required: false
default: ""


runs:
using: composite

steps:


- name: Configure AWS credentials using the OpenID Connect (OIDC) provider 🔑
uses: aws-actions/configure-aws-credentials@ececac1a45f3b08a01d2dd070d28d111c5fe6722 # v4.1.0
with:
Expand All @@ -61,12 +64,27 @@ runs:


- name: Download existing ECS task definition ⚙️
id: get-task-info
shell: bash
env:
TASK_DEFINITION_NAME: "${{ inputs.task-definition-name }}"
run: |
aws ecs describe-task-definition \
--task-definition "${{ inputs.task-definition-name }}" \
--query taskDefinition \
| jq 'del(.registeredAt, .registeredBy, .compatibilities, .taskDefinitionArn, .requiresAttributes, .revision, .status)' > "task-definition.json"
TASK_RESPONSE=$(aws ecs describe-task-definition \
--task-definition "$TASK_DEFINITION_NAME" \
--include TAGS)

# Remove unnecessary fields from the task definition
echo "$TASK_RESPONSE" | jq '.taskDefinition |
del(.registeredAt, .registeredBy, .compatibilities, .taskDefinitionArn,
.requiresAttributes, .revision, .status)' > "task-definition.json"

# Get tags from the API response
TAGS=$(echo "$TASK_RESPONSE" | jq '.tags')

# Add tags to task definition
jq --argjson tags "$TAGS" '. + {tags: $tags}' "task-definition.json" > "temp.json" && mv "temp.json" "task-definition.json"

echo "✅ Task definition with tags saved to task-definition.json"


- name: Update ECS task definition with new image URIs ⚙️
Expand All @@ -79,7 +97,7 @@ runs:
const fs = require('fs');

// Set filenames
const taskDefFile = 'task-definition.json';
const taskDefFile = "task-definition.json";
const outputTaskDefFile = 'updated-task-definition.json';

// Read task definition
Expand Down Expand Up @@ -130,7 +148,6 @@ runs:
// Set output
core.setOutput('task-definition', outputTaskDefFile);


- if: inputs.deploy == 'true'
name: Deploy task definition 🚀
id: deploy
Expand All @@ -142,6 +159,39 @@ runs:
wait-for-service-stability: "${{ inputs.wait-for-service-stability }}"


- if: inputs.deploy == 'true' && inputs.images-ssm-parameter-name != ''
name: Write deployed images to SSM parameter 📝
shell: bash
env:
DEPLOYED_IMAGES: ${{ inputs.images }}
SSM_PARAMETER_NAME: ${{ inputs.images-ssm-parameter-name }}
run: |
echo "Deployed images: (This is the input image to this workflow.)"
echo $DEPLOYED_IMAGES | jq
echo

CURRENT_IMAGES=$(aws ssm get-parameter \
--name "$SSM_PARAMETER_NAME" \
--query "Parameter.Value" \
--output text)
echo "Current images from SSM: (These are all images stored for this task definition, before deploying.)"
echo $CURRENT_IMAGES | jq
Comment on lines +176 to +178

Copilot AI Apr 11, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The command does not handle cases where the SSM parameter does not exist, which could cause the script to fail. Consider adding error handling or providing a default value for CURRENT_IMAGES if the parameter is missing.

Suggested change
--output text)
echo "Current images from SSM: (These are all images stored for this task definition, before deploying.)"
echo $CURRENT_IMAGES | jq
--output text 2>/dev/null || echo "[]")
if [ "$CURRENT_IMAGES" == "[]" ]; then
echo "SSM parameter '$SSM_PARAMETER_NAME' does not exist or is empty. Using default value: []"
else
echo "Current images from SSM: (These are all images stored for this task definition, before deploying.)"
echo $CURRENT_IMAGES | jq
fi

Copilot uses AI. Check for mistakes.
echo

MERGED_IMAGES=$(echo "$CURRENT_IMAGES" | jq --argjson new "$DEPLOYED_IMAGES" '. + $new')
echo "Current images from SSM merged with deployed images:"
echo $MERGED_IMAGES | jq
echo

aws ssm put-parameter \
--name "$SSM_PARAMETER_NAME" \
--type "String" \
--value "$MERGED_IMAGES" \
--overwrite

echo "✅ Wrote deployed images to SSM parameter '$SSM_PARAMETER_NAME'"


- if: inputs.deploy == 'false'
name: Write simple summary for non-deployment 📝
shell: bash
Expand All @@ -160,6 +210,7 @@ runs:
ECR_REGISTRY: ${{ steps.ecr-login.outputs.registry }}
TASK_DEFINITION_NAME: ${{ inputs.task-definition-name }}
TASK_DEFINITION_ARN: ${{ steps.deploy.outputs.task-definition-arn }}
IMAGES_SSM_PARAMETER_NAME: ${{ inputs.images-ssm-parameter-name }}
run: |
# To test the summary locally, copy the rest of this workflow into a script, uncomment the variables, and run.
#
Expand All @@ -172,12 +223,13 @@ runs:
#CLUSTER_NAME='pirates-dev'
#TASK_DEFINITION_NAME='too-tikki'
#TASK_DEFINITION_ARN="arn:aws:ecs:$AWS_REGION:$AWS_ACCOUNT_ID:task-definition/$TASK_DEFINITION_NAME:143"
#IMAGES_SSM_PARAMETER_NAME="/ecs/pirates-dev/too-tikki/images"

IMAGE_COUNT=$(echo "$IMAGES_JSON" | jq 'length')
AWS_ACCOUNT_ID=$(echo "$ECR_REGISTRY" | cut -d. -f1)
SERVICE_URL="https://$AWS_REGION.console.aws.amazon.com/ecs/v2/clusters/$CLUSTER_NAME/services/$SERVICE_NAME?region=$AWS_REGION"
TASK_REVISION=$(echo "$TASK_DEFINITION_ARN" | cut -d ':' -f 7)
TASK_DEF_URL="https://$AWS_REGION.console.aws.amazon.com/ecs/v2/task-definitions/$TASK_DEFINITION_NAME/$TASK_REVISION/containers?region=$AWS_REGION"
TASK_DEFINITION_REVISION=$(echo "$TASK_DEFINITION_ARN" | cut -d ':' -f 7)
TASK_DEF_URL="https://$AWS_REGION.console.aws.amazon.com/ecs/v2/task-definitions/$TASK_DEFINITION_NAME/$TASK_DEFINITION_REVISION/containers?region=$AWS_REGION"
CONTAINER_ROWS=$(echo "$IMAGES_JSON" | jq -r 'to_entries[] | "| \(.key) | \(.value.imageRepository) | `\(.value.imageDigest)` | `\(.value.imageTag)` |"')

#######################################
Expand All @@ -188,16 +240,21 @@ runs:

## Deployment summary 📋

Updated task definition successfully.
Successfully updated task definition.

| Item | Link |
|-------------------|----------------------------------------------------------|
| ECS service | [$SERVICE_NAME]($SERVICE_URL) |
| Task definition | [$TASK_DEFINITION_NAME:$TASK_REVISION]($TASK_DEF_URL) |
|-------------------|---------------------------------------------------------------------|
| ECS service | [$SERVICE_NAME]($SERVICE_URL) |
| Task definition | [$TASK_DEFINITION_NAME:$TASK_DEFINITION_REVISION]($TASK_DEF_URL) |

### Container(s) updated 📦

| Container | Repository | Digest | Tag |
| --- | --- | --- | --- |
| Container | ECR repository | Digest | Tag |
| --------- | -------------- | ------ | --- |
$CONTAINER_ROWS

EOF

if [ -n "$IMAGES_SSM_PARAMETER_NAME" ]; then
SSM_URL="https://$AWS_REGION.console.aws.amazon.com/systems-manager/parameters/$IMAGES_SSM_PARAMETER_NAME/description?region=$AWS_REGION"
echo "✅ Successfully wrote image metadata to SSM parameter [$IMAGES_SSM_PARAMETER_NAME]($SSM_URL)." >> $GITHUB_STEP_SUMMARY
fi