Skip to content

Commit 09f0561

Browse files
authored
feat: ECS Partial Task Definitions & GitHub OIDC setup (#687)
1 parent 36c52a9 commit 09f0561

File tree

5 files changed

+242
-9
lines changed

5 files changed

+242
-9
lines changed

docs/layers/github-actions/github-oidc-with-aws.mdx

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,18 @@ sequenceDiagram
8888
### <StepNumber/> Deploy GitHub OIDC Provider Component
8989

9090
After deploying the [GitHub OIDC Provider component](/components/library/aws/github-oidc-provider/) into an account, you should see the Identity Provider in IAM in the AWS Web Console.
91+
92+
Deploy this component in each account where GitHub Actions need to assume a role.
93+
94+
<Steps>
95+
- Import `catalog/github-oidc-provider` in the `gbl` stack for the given account
96+
- Deploy the `github-oidc-provider` component: `atmos terraform apply github-oidc-provider -s plat-gbl-dev`
97+
</Steps>
98+
9199
</Step>
92100

93101
<Step>
94-
### <StepNumber/> Configure GitHub OIDC Mixin Role and Policy
102+
### <StepNumber/> Option 1: Configure GitHub OIDC Mixin Role and Policy
95103

96104
Use the mixin to grant GitHub the ability to assume a role for a specific component.
97105

@@ -100,7 +108,7 @@ sequenceDiagram
100108
</Step>
101109

102110
<Step>
103-
### <StepNumber/> Deploy GitHub OIDC Role Component
111+
### <StepNumber/> Option 2: Deploy GitHub OIDC Role Component
104112

105113
Deploy the [GitHub OIDC Role component](/components/library/aws/github-oidc-role/) to create a generalized role for GitHub to access several resources in AWS.
106114
</Step>
Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
---
2+
title: "ECS Partial Task Definitions"
3+
sidebar_label: "Partial Task Definitions"
4+
sidebar_position: 20
5+
---
6+
7+
import Intro from '@site/src/components/Intro';
8+
import Steps from '@site/src/components/Steps';
9+
10+
<Intro>
11+
This document describes what partial task definitions are and how we can use them to set up ECS services using Terraform and GitHub Actions.
12+
</Intro>
13+
14+
## The Problem
15+
16+
Managing ECS Services is challenging. Ideally, we want our services to be managed by Terraform so everything is living
17+
in code. However, we also want to update the task definition via GitOps as through the GitHub release lifecycle. This is
18+
challenging because Terraform can create the task definition, but if updated by the application repository, the task
19+
definition will be out of sync with the Terraform state.
20+
21+
Managing it entirely through Terraform means we cannot easily update the newly built image by the application repository
22+
unless we directly commit to the infrastructure repository, which is not ideal.
23+
24+
Managing it entirely through the application repository means we cannot codify the infrastructure and have to hardcode
25+
ARNs, secrets, and other infrastructure-specific configurations.
26+
27+
## Introduction
28+
29+
ECS Partial task definitions is the idea of breaking the task definition into smaller parts. This allows for easier
30+
management of the task definition and makes it easier to update the task definition.
31+
32+
We do this by setting up Terraform to manage a portion of the task definition, and the application repository to manage
33+
another portion.
34+
35+
The Terraform (infrastructure) portion is created first. It will create an ECS Service in ECS, and then upload the task
36+
definition JSON to S3 as `task-template.json`.The application repository will have a `task-definition.json` git
37+
controlled, during the development lifecycle, the application repository will download the task definition from S3,
38+
merge the task definitions, then update the ECS Service with the new task definition. Finally, GitHub actions will
39+
update the S3 bucket with the deployed task definition under `task-definition.json`. If Terraform is planned again, it
40+
will use the new task definition as the base for the next deployment, thus not resetting the image or application
41+
configuration.
42+
43+
<img src="/assets/ecs-partial-task-definitions.png" /><br/>
44+
45+
### Pros
46+
47+
The **benefit** to using this approach is that we can manage the task definition portion in Terraform with the
48+
infrastructure, meaning secrets, volumes, and other ARNs can be managed in Terraform. If a filesystem ID updates we can
49+
re-apply Terraform to update the task definition with the new filesystem ID. The application repository can manage the
50+
container definitions, environment variables, and other application-specific configurations. This allows developers who
51+
are closer to the application to quickly update the environment variables or other configuration.
52+
53+
### Cons
54+
55+
The drawback to this approach is that it is more complex than managing the task definition entirely in Terraform or the
56+
application repository. It requires more setup and more moving parts. It can be confusing for a developer who is not
57+
familiar with the setup to understand how the task definition is being managed and deployed.
58+
59+
This also means that when something goes wrong, it becomes harder to troubleshoot as there are more moving parts.
60+
61+
### Getting Setup
62+
63+
#### Pre-requisites
64+
65+
- Application Repository - [Cloud Posse Example ECS Application](https://github.com/cloudposse-examples/app-on-ecs)
66+
- Infrastructure Repository
67+
- ECS Cluster - [Cloud Posse Docs](https://docs.cloudposse.com/components/library/aws/ecs/) -
68+
[Component](https://github.com/cloudposse/Terraform-aws-components/tree/main/modules/ecs).
69+
- `ecs-service` - [Cloud Posse Docs](https://docs.cloudposse.com/components/library/aws/ecs-service/) -
70+
[Component](https://github.com/cloudposse/Terraform-aws-components/tree/main/modules/ecs-service).
71+
- **Must** use the Cloud Posse Component.
72+
- [`v1.416.0`](https://github.com/cloudposse/Terraform-aws-components/releases/tag/1.416.0) or later.
73+
- S3 Bucket - [Cloud Posse Docs](https://docs.cloudposse.com/components/library/aws/s3-bucket/) -
74+
[Component](https://github.com/cloudposse/Terraform-aws-components/tree/main/modules/s3-bucket).
75+
76+
#### Steps
77+
78+
<Steps>
79+
80+
1. Set up the S3 Bucket that will store the task definition.
81+
82+
<br/>This bucket should be in the same account as the ECS Cluster.
83+
84+
<br/>
85+
<details>
86+
<summary>S3 Bucket Default Definition</summary>
87+
88+
```yaml
89+
components:
90+
terraform:
91+
s3-bucket/defaults:
92+
metadata:
93+
type: abstract
94+
vars:
95+
enabled: true
96+
account_map_tenant_name: core
97+
# Suggested configuration for all buckets
98+
user_enabled: false
99+
acl: "private"
100+
grants: null
101+
force_destroy: false
102+
versioning_enabled: false
103+
allow_encrypted_uploads_only: true
104+
block_public_acls: true
105+
block_public_policy: true
106+
ignore_public_acls: true
107+
restrict_public_buckets: true
108+
allow_ssl_requests_only: true
109+
lifecycle_configuration_rules:
110+
- id: default
111+
enabled: true
112+
abort_incomplete_multipart_upload_days: 90
113+
filter_and:
114+
prefix: ""
115+
tags: {}
116+
# Move to Glacier after 2 years
117+
transition:
118+
- storage_class: GLACIER
119+
days: 730
120+
# Never expire
121+
expiration: {}
122+
# Versioning isnt enabled, but these default values are still required
123+
noncurrent_version_transition:
124+
- storage_class: GLACIER
125+
days: 90
126+
noncurrent_version_expiration: {}
127+
```
128+
129+
</details>
130+
131+
```yaml
132+
import:
133+
- catalog/s3-bucket/defaults
134+
135+
components:
136+
terraform:
137+
s3-bucket/ecs-tasks-mirror: #NOTE this is the component instance name.
138+
metadata:
139+
component: s3-bucket
140+
inherits:
141+
- s3-bucket/defaults
142+
vars:
143+
enabled: true
144+
name: ecs-tasks-mirror
145+
```
146+
147+
2. Create an ECS Service in Terraform
148+
149+
<br/>Set up the ECS Service in Terraform using the
150+
[`ecs-service` component](https://github.com/cloudposse/Terraform-aws-components/tree/main/modules/ecs-service). This
151+
will create the ECS Service and upload the task definition to the S3 bucket.
152+
153+
<br/>To enable Partial Task Definitions, set the variable `s3_mirror_name` to be the component instance name of the
154+
bucket to mirror to. For example `s3-bucket/ecs-tasks-mirror`
155+
156+
```yaml
157+
components:
158+
terraform:
159+
ecs-services/defaults:
160+
metadata:
161+
component: ecs-service
162+
type: abstract
163+
vars:
164+
enabled: true
165+
ecs_cluster_name: "ecs/cluster"
166+
s3_mirror_name: s3-bucket/ecs-tasks-mirror
167+
```
168+
169+
3. Set up an Application repository with GitHub workflows.
170+
171+
An example application repository can be found [here](https://github.com/cloudposse-examples/app-on-ecs).
172+
173+
<br/> Two things need to be pulled from this repository:
174+
175+
- The `task-definition.json` file under `deploy/task-definition.json`
176+
- The GitHub Workflows.
177+
178+
An important note about the GitHub Workflows, in the example repository they all live under `.github/workflows`. This
179+
is done so development of workflows can be fast, however we recommend moving the shared workflows to a separate
180+
repository and calling them from the application repository. The application repository should only contain the
181+
workflows `main-branch.yaml`, `release.yaml` and `feature-branch.yml`.
182+
183+
<br/>To enable Partial Task Definitions in the workflows, the call to
184+
[`cloudposse/github-action-run-ecspresso` (link)](https://github.com/cloudposse-examples/app-on-ecs/blob/main/.github/workflows/workflow-cd-ecspresso.yml#L133-L147)
185+
should have the input `mirror_to_s3_bucket` set to the S3 bucket name. the variable `use_partial_taskdefinition`
186+
should be set to `'true'`
187+
188+
<details>
189+
<summary> Example GitHub Action Step </summary>
190+
191+
```yaml
192+
- name: Deploy
193+
uses: cloudposse/github-action-deploy-ecspresso@0.6.0
194+
continue-on-error: true
195+
if: ${{ steps.db_migrate.outcome != 'failure' }}
196+
id: deploy
197+
with:
198+
image: ${{ steps.image.outputs.out }}
199+
image-tag: ${{ inputs.tag }}
200+
region: ${{ steps.environment.outputs.region }}
201+
operation: deploy
202+
debug: false
203+
cluster: ${{ steps.environment.outputs.cluster }}
204+
application: ${{ steps.environment.outputs.name }}
205+
taskdef-path: ${{ inputs.path }}
206+
mirror_to_s3_bucket: ${{ steps.environment.outputs.s3-bucket }}
207+
use_partial_taskdefinition: "true"
208+
timeout: 10m
209+
```
210+
211+
</details>
212+
213+
</Steps>
214+
215+
## Operation
216+
217+
Changes through Terraform will not immediately be reflected in the ECS Service. This is because the task template has
218+
been updated, but whatever was in the `task-definition.json` file in the S3 bucket will be used for deployment.
219+
220+
To update the ECS Service after updating the Terraform for it, you must deploy through GitHub Actions. This will then
221+
download the new template and create a new updated `task-defintion.json` to store in s3.

docs/layers/software-delivery/ecs-ecspresso/setup.mdx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ import AtmosWorkflow from '@site/src/components/AtmosWorkflow';
2323
| 3. Validate the environment configuration | Click Ops |
2424
| 4. Create a GitHub PAT | Click Ops |
2525
| 5. Set all Example App repository secrets | Click Ops |
26-
| 6. Deploy the example ECS services | `atmos workflow deploy/app-on-ecs -f app-on-ecs` |
26+
| 6. Deploy the shared ECS Task Definition S3 Bucket | `atmos apply s3-bucket/ecs-tasks-mirror -s < YOUR STACK >` |
27+
| 7. Deploy the example ECS services | `atmos workflow deploy/app-on-ecs -f app-on-ecs` |
2728

2829
<Note title="Note">
2930

@@ -306,6 +307,12 @@ We do not recommend keeping all shared workflows in the same repository as in th
306307
</dl>
307308
</Step>
308309

310+
<Step>
311+
### <StepNumber/> Configure the S3 Mirror Bucket, if not already configured
312+
313+
If you haven't already configured the S3 mirror bucket, deploy and configure the shared S3 bucket for ECS tasks definitions now. Follow the [ECS Partial Task Definitions guide](/layers/software-delivery/ecs-ecspresso/ecs-partial-task-definitions/#steps)
314+
</Step>
315+
309316
<Step>
310317
### <StepNumber/> Deploy the Example App ECS Service
311318

@@ -368,7 +375,7 @@ We do not recommend keeping all shared workflows in the same repository as in th
368375

369376
</details>
370377

371-
Apply this component with the following:
378+
Finally, apply the `ecs-services/example-app-on-ecs` component to deploy the Example App ECS service.
372379

373380
<AtmosWorkflow workflow="deploy/app-on-ecs" fileName="app-on-ecs" />
374381
</Step>

package-lock.json

Lines changed: 2 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
117 KB
Loading

0 commit comments

Comments
 (0)