-
Notifications
You must be signed in to change notification settings - Fork 2
/
action.yml
371 lines (348 loc) · 18.4 KB
/
action.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
name: "Pipelines Dispatch"
description: "Dispatch Terragrunt plan/apply/destroy jobs with Gruntwork Pipelines"
inputs:
account_id:
description: "The AWS Account in which the action will run"
required: true
repo:
description: "The infrastructure-pipelines repo where jobs should be dispatched"
required: true
repo_owner:
description: "The owner of the infrastructure-pipelines repo where jobs should be dispatched"
required: true
branch:
description: "The branch against which Pipelines will run the action"
required: true
working_directory:
description: "The directory in which Terragrunt will run the action"
required: true
command:
description: "The command (e.g., plan, apply, etc) for Terragrunt to run"
required: true
args:
description: "The arguments to pass into Terragrunt"
required: true
polling_interval_ms:
description: "The interval, in milliseconds, to poll for the status of the dispatched job. Keep in mind that each poll will count against your GitHub Actions API rate limit. The default is 1 minute(60000 milliseconds)"
required: false
default: "60000"
infra_pipelines_token:
description: "GitHub PAT which has access to run GitHub Actions Workflows in your infrastructure-pipelines repository"
required: true
infra_live_token:
description: "GitHub PAT which has access to create issues and PR comments in your infrastructure-live repository"
required: true
change_type:
description: The change type for the infra change, as detected by pipelines orchestrate
required: true
# These are the valid options, but it's not enforced by GitHub Actions
# options:
# - ModuleChanged
# - ModuleDeleted
# - ModuleAdded
# - ModulesAdded
# - EnvCommonChanged
# - EnvCommonDeleted
# - EnvCommonAdded
# - AccountRequested
# - TeamAccountsRequested
# - AccountAdded
# - TeamAccountsAdded
# - AccountChanged
# - AccountDeleted
# - PipelinesPermissionAdded
# - PipelinesPermissionChanged
# - PipelinesPermissionDeleted
# - PipelinesEnvCommonPermissionAdded
# - PipelinesEnvCommonPermissionChanged
# - PipelinesEnvCommonPermissionDeleted
additional_data:
description: "Change Type specific data"
required: false
new_accounts:
description: "The new accounts to be created"
required: false
actor:
description: "The github actor responsible for the change"
required: true
presign_token:
description: "Presign a `GetCallerIdentity` request to authenticate this repo to the `infrastructure-pipelines` repo"
required: false
default: "false"
install_pipelines_cli:
description: "Install the Gruntwork Pipelines CLI. Ignored if `presign_token` is `false`"
required: false
default: "true"
assume_pipelines_auth_role:
description: "Assume the role necessary to run `pipelines auth presign`. Ignored if `presign_token` is `false`"
required: false
default: "true"
pipelines_token:
description: "The GitHub token for downloading the Gruntwork Pipelines binary"
required: false
pipelines_cli_version:
description: "The version of the Gruntwork Pipelines CLI to use"
required: false
default: "v0.6.1"
pipelines_auth_role:
description: "The role to assume before running `pipelines auth presign`, which is used to authenticate the calling repo. Only necessary when pipelines auth role is provisioned and checked in infrastructure-pipelines repo."
required: false
pipelines_auth_region:
description: "The region in which the role to assume before running `pipelines auth presign`. Only necessary when pipelines auth role is provisioned and checked in infrastructure-pipelines repo."
required: false
default: "us-east-1"
runs:
using: "composite"
steps:
- name: Validate Presign Inputs
shell: bash
if: inputs.presign_token == 'true'
env:
INSTALL_PIPELINES_CLI: ${{ inputs.install_pipelines_cli }}
ASSUME_PIPELINES_AUTH_ROLE: ${{ inputs.assume_pipelines_auth_role }}
PIPELINES_TOKEN: ${{ inputs.pipelines_token }}
PIPELINES_AUTH_ROLE: ${{ inputs.pipelines_auth_role }}
run: |
if [[ "$INSTALL_PIPELINES_CLI" == "true" && -z "$PIPELINES_TOKEN" ]]; then
echo "ERROR: You must provide a GitHub token for downloading the Gruntwork Pipelines CLI"
exit 1
fi
if [[ "$ASSUME_PIPELINES_AUTH_ROLE" == "true" && -z "$PIPELINES_AUTH_ROLE" ]]; then
echo "ERROR: You must provide a role to assume before running `pipelines auth presign`"
exit 1
fi
- name: Check for Minimum Supported Version of the Pipelines CLI
shell: bash
env:
PIPELINES_CLI_VERSION: ${{ inputs.pipelines_cli_version }}
ACTION_PATH: ${{ github.action_path }}
run: $ACTION_PATH/scripts/check-msv.sh
# We only need to install the pipelines CLI if we're presigning a token
# and we have a token to use to download the CLI. Clients might decide to
# install the CLI before running this action, in which case we don't need
# to do it here.
- name: Download Pipelines CLI
if: ${{ inputs.presign_token == 'true' && inputs.install_pipelines_cli == 'true' }}
uses: dsaltares/[email protected]
with:
repo: "gruntwork-io/pipelines-cli"
version: "tags/${{ inputs.pipelines_cli_version }}"
file: "pipelines_linux_amd64"
target: "/tmp/pipelines"
token: ${{ inputs.pipelines_token }}
- name: Install Pipelines CLI
if: ${{ inputs.presign_token == 'true' && inputs.install_pipelines_cli == 'true' }}
shell: bash
run: |
sudo mv /tmp/pipelines /usr/local/bin/pipelines
sudo chmod +x /usr/local/bin/pipelines
# We only need to authenticate with AWS here if we're presigning a token
# and we have a role to assume to do so. Clients might decide to assume a role before
# running this action, in which case we don't need to do it here.
- name: Authenticate with AWS
if: ${{ inputs.presign_token == 'true' && inputs.assume_pipelines_auth_role == 'true' }}
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::${{ inputs.account_id }}:role/${{ inputs.pipelines_auth_role }}
aws-region: ${{ inputs.pipelines_auth_region }}
- name: Setup Action Configurations
id: setup-action-configurations
shell: bash
env:
CHANGE_TYPE: ${{ inputs.change_type }}
MANAGEMENT_ACCOUNT: ${{ inputs.account_id }}
NEW_ACCOUNT_NAME: ${{ fromJSON(inputs.additional_data).AccountName }}
BRANCH: ${{ inputs.branch }}
INFRA_LIVE_REPO: ${{ github.repository }}
WORKING_DIRECTORY: ${{ inputs.working_directory }}
COMMAND: ${{ inputs.command }}
ARGS: ${{ inputs.args }}
CHILD_ACCOUNT_ID: ${{ fromJSON(inputs.additional_data).ChildAccountId }}
PRESIGN_TOKEN: ${{ inputs.presign_token }}
TEAM_ACCOUNT_NAMES: ${{ fromJSON(inputs.additional_data).TeamAccountNames }}
NEW_ACCOUNTS: ${{ inputs.new_accounts }}
ACTION_PATH: ${{ github.action_path }}
# We need this magic value found here:
# https://docs.github.com/en/actions/learn-github-actions/contexts#github-context
run: $ACTION_PATH/scripts/setup-action-configs.sh
- name: Dispatch default workflow and get the run ID
env:
GH_TOKEN: ${{ inputs.infra_pipelines_token }}
uses: codex-/[email protected]
id: return_dispatch
with:
token: ${{ inputs.infra_pipelines_token }}
ref: "refs/heads/main"
repo: ${{ inputs.repo }}
owner: ${{ inputs.repo_owner }}
workflow: ${{ steps.setup-action-configurations.outputs.workflow }}
workflow_inputs: ${{ steps.setup-action-configurations.outputs.workflow_inputs }}
# Await default workflow run
- name: Await Run ID ${{ steps.return_dispatch.outputs.run_id }}
env:
GH_TOKEN: ${{ inputs.infra_pipelines_token }}
uses: Codex-/[email protected]
with:
token: ${{ inputs.infra_pipelines_token }}
repo: ${{ inputs.repo }}
owner: ${{ inputs.repo_owner }}
run_id: ${{ steps.return_dispatch.outputs.run_id }}
run_timeout_seconds: 3600 # one hour
poll_interval_ms: ${{ inputs.polling_interval_ms }}
- name: Completed
if: ${{ always() }}
shell: bash
env:
COMMAND: ${{ inputs.command }}
WORKING_DIR: ${{ inputs.working_directory }}
ACCOUNT_ID: ${{ inputs.account_id }}
REPO: ${{ inputs.repo }}
REPO_OWNER: ${{ inputs.repo_owner }}
run: echo "::notice ::Completed $COMMAND for $WORKING_DIR in acct $ACCOUNT_ID%0AFor details see https://github.com/$REPO_OWNER/$REPO/actions/runs/${{ steps.return_dispatch.outputs.run_id }}"
- name: Get Job ID & Terragrunt Run URL
if: ${{ always() }} # This ensures this step always runs regardless of failure of previous step
id: jobs_data
env:
GH_TOKEN: ${{ inputs.infra_pipelines_token }}
REPO: ${{ inputs.repo }}
REPO_OWNER: ${{ inputs.repo_owner }}
shell: bash
run: |
RUN_ID=${{ steps.return_dispatch.outputs.run_id }}
JOB_ID="$(gh api /repos/$REPO_OWNER/$REPO/actions/runs/$RUN_ID/jobs | jq .jobs[0].id)"
JOB_OUTPUT=$(gh api /repos/$REPO_OWNER/$REPO/actions/jobs/$JOB_ID)
STEP_NUMBER=$(echo "$JOB_OUTPUT" | jq -c '[.steps[] | select(.name | startswith("Run terragrunt")).number] | last')
STEP_FRAGMENT="#step:$STEP_NUMBER:1" # Link to first line of the Run Terragrunt Step
TERRAGRUNT_STEP_URL="https://github.com/$REPO_OWNER/$REPO/actions/runs/$RUN_ID/job/$JOB_ID$STEP_FRAGMENT"
echo "run_id=$RUN_ID" >> "$GITHUB_OUTPUT"
echo "job_id=$JOB_ID" >> "$GITHUB_OUTPUT"
echo "terragrunt_step_url=$TERRAGRUNT_STEP_URL" >> "$GITHUB_OUTPUT"
- name: Retrieve job run results
id: get_job_run_results
if: ${{ always() }}
env:
GH_TOKEN: ${{ inputs.infra_pipelines_token }}
JOB_ID: ${{ steps.jobs_data.outputs.job_id }}
REPO: ${{ inputs.repo }}
REPO_OWNER: ${{ inputs.repo_owner }}
shell: bash
run: |
TERRAGRUNT_SUCCESS=$(gh run --repo $REPO_OWNER/$REPO view --job $JOB_ID --json jobs | jq -r '[.jobs[0].steps[] | select(.name | startswith("Run terragrunt")).conclusion] | all(.[] == "success"; .)')
if [[ "$TERRAGRUNT_SUCCESS" == "true" ]]; then TERRAGRUNT_STEP_EMOJI=":white_check_mark:"; TERRAGRUNT_STEP_STATUS="succeeded"; fi
if [[ "$TERRAGRUNT_SUCCESS" == "false" ]]; then TERRAGRUNT_STEP_EMOJI=":x:"; TERRAGRUNT_STEP_STATUS="failed"; fi
FAILED_STEPS=$(gh run --repo $REPO_OWNER/$REPO view --job $JOB_ID --json jobs | jq -r '.jobs[0].steps[] | select(.name | startswith("Run terragrunt") | not) | select(.conclusion == "failure")')
echo "terragrunt_step_status=$TERRAGRUNT_STEP_STATUS" >> $GITHUB_OUTPUT
echo "terragrunt_step_emoji=$TERRAGRUNT_STEP_EMOJI" >> $GITHUB_OUTPUT
EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64)
echo "failed_steps<<$EOF" >> $GITHUB_OUTPUT
echo "$FAILED_STEPS" >> $GITHUB_OUTPUT
echo "$EOF" >> $GITHUB_OUTPUT
- name: Post PR comments
if: ${{ always() && contains(inputs.command, 'plan') }}
env:
GH_TOKEN: ${{ inputs.infra_live_token }}
REPO: ${{ inputs.repo }}
REPO_OWNER: ${{ inputs.repo_owner }}
WORKING_DIR: ${{ inputs.working_directory }}
RUN_ID: ${{ steps.jobs_data.outputs.run_id }}
JOB_ID: ${{ steps.jobs_data.outputs.job_id }}
TERRAGRUNT_STEP_URL: ${{ steps.jobs_data.outputs.terragrunt_step_url }}
TERRAGRUNT_STEP_STATUS: ${{ steps.get_job_run_results.outputs.terragrunt_step_status }}
TERRAGRUNT_STEP_EMOJI: ${{ steps.get_job_run_results.outputs.terragrunt_step_emoji }}
FAILED_STEPS: ${{ steps.get_job_run_results.outputs.failed_steps }}
PR_NUMBER: ${{ github.event.number }}
shell: bash
run: |
if [ $TERRAGRUNT_STEP_STATUS = 'succeeded' ]; then
COPY="_To **apply** all changes, merge this pull request._"$'\n'"_On merge, another comment will be added to this pull request with apply logs._"
else
COPY="_To **re-run** the plan, push a new set of changes to this pull request's branch._"
fi
# Post PR comment indicating terragrunt plan success/failure
COMMENT_BODY=$'## Terragrunt Plan\n\n'"$TERRAGRUNT_STEP_EMOJI Terragrunt Plan $TERRAGRUNT_STEP_STATUS for "$'`'"$WORKING_DIR"$'`.\n\n '"$COPY"$'\n\n[View logs]('"$TERRAGRUNT_STEP_URL"$')'
if [[ -n "$TERRAGRUNT_STEP_STATUS" ]]; then gh issue comment $PR_NUMBER --body "$COMMENT_BODY"; fi
# If steps other than terragrunt plan failed, post PR comment about a pipeline failure.
# Include links to each of the failed steps.
if [[ -n "$FAILED_STEPS" ]]; then
readarray -t FAILED_STEP_NUMBERS < <(echo $FAILED_STEPS | jq -r .number)
readarray -t FAILED_STEP_NAMES < <(echo $FAILED_STEPS | jq -r .name)
COMMENT_BODY=$'## Pipelines Run Failed\n\n:x: Pipelines run failed for `'"$WORKING_DIR"$'`.\n\nInspect the logs associated with each of the following failures to learn the details of each failure:\n\n'
for i in "${!FAILED_STEP_NUMBERS[@]}"; do
LINK=$'- ['"${FAILED_STEP_NAMES[$i]}"$']('"https://github.com/$REPO_OWNER/$REPO/actions/runs/$RUN_ID/job/$JOB_ID"$'#step:'"${FAILED_STEP_NUMBERS[$i]}"$':1)'
COMMENT_BODY="$COMMENT_BODY""$LINK"$'\n'
done
gh issue comment $PR_NUMBER --body "$COMMENT_BODY"
echo "COMMENTED"
fi
- name: Post Account Deletion Warning
if: ${{ always() && contains(inputs.command, 'plan') && inputs.change_type == 'AccountDeleted' }}
env:
GH_TOKEN: ${{ inputs.infra_live_token }}
REPO: ${{ inputs.repo }}
REPO_OWNER: ${{ inputs.repo_owner }}
PR_NUMBER: ${{ github.event.number }}
shell: bash
run: |
COMMENT_BODY=$'## Account Deletion Warning\n\n:warning: **Account Deletion** Detected.\n\nThis action will remove the account from management in Control Tower.\n\n**Please ensure that only the appropriate account request(s) have been deleted, and that the plan above properly reflects the intended account removal(s) in Control Tower.**\n\nThis **does not** close the account in AWS, however.\n\nTo complete the process of closing the account, follow the instructions [here](https://github.com/orgs/gruntwork-io/discussions/797#discussioncomment-8058207) after merging this pull request.'
gh issue comment $PR_NUMBER --body "$COMMENT_BODY"
- name: Get requesting PR number
id: get_pr_number
# Only try this if we've done an apply, which means the PR has been merged
if: ${{ always() && contains(inputs.command, 'apply') }}
shell: bash
env:
GH_TOKEN: ${{ inputs.infra_live_token }}
COMMIT_SHA: ${{ inputs.branch }}
run: |
PR_NUMBER=$(gh pr list --search $COMMIT_SHA --state merged --json number | jq '.[].number')
echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT
- name: Post PR comments on apply
if: ${{ always() && contains(inputs.command, 'apply') }}
env:
GH_TOKEN: ${{ inputs.infra_live_token }}
REPO: ${{ inputs.repo }}
REPO_OWNER: ${{ inputs.repo_owner }}
WORKING_DIR: ${{ inputs.working_directory }}
RUN_ID: ${{ steps.jobs_data.outputs.run_id }}
JOB_ID: ${{ steps.jobs_data.outputs.job_id }}
TERRAGRUNT_STEP_URL: ${{ steps.jobs_data.outputs.terragrunt_step_url }}
TERRAGRUNT_STEP_STATUS: ${{ steps.get_job_run_results.outputs.terragrunt_step_status }}
TERRAGRUNT_STEP_EMOJI: ${{ steps.get_job_run_results.outputs.terragrunt_step_emoji }}
FAILED_STEPS: ${{ steps.get_job_run_results.outputs.failed_steps }}
PR_NUMBER: ${{ steps.get_pr_number.outputs.pr_number }}
shell: bash
run: |
if [ $TERRAGRUNT_STEP_STATUS = 'succeeded' ]; then
COPY="_**Apply** ran on the \`${{ github.ref_name }}\` branch after this pull request was merged._"
else
COPY="_To recover from the failure, check the logs, push any required changes to a new branch, then create a new pull request._"
fi
# Post PR comment indicating terragrunt apply success/failure
COMMENT_BODY=$'## Terragrunt Apply\n\n'"$TERRAGRUNT_STEP_EMOJI Terragrunt Apply $TERRAGRUNT_STEP_STATUS for "$'`'"$WORKING_DIR"$'`.\n\n '"$COPY"$'\n\n[View logs]('"$TERRAGRUNT_STEP_URL"$')'
if [[ -n "$TERRAGRUNT_STEP_STATUS" ]]; then gh issue comment $PR_NUMBER --body "$COMMENT_BODY"; fi
# If steps other than terragrunt apply failed, post PR comment about a pipeline failure.
# Include links to each of the failed steps.
if [[ -n "$FAILED_STEPS" ]]; then
readarray -t FAILED_STEP_NUMBERS < <(echo $FAILED_STEPS | jq -r .number)
readarray -t FAILED_STEP_NAMES < <(echo $FAILED_STEPS | jq -r .name)
COMMENT_BODY=$'## Pipelines Run Failed\n\n:x: Pipelines run failed for `'"$WORKING_DIR"$'`.\n\nInspect the logs associated with each of the following failures to learn the details of each failure:\n\n'
for i in "${!FAILED_STEP_NUMBERS[@]}"; do
LINK=$'- ['"${FAILED_STEP_NAMES[$i]}"$']('"https://github.com/$REPO_OWNER/$REPO/actions/runs/$RUN_ID/job/$JOB_ID"$'#step:'"${FAILED_STEP_NUMBERS[$i]}"$':1)'
COMMENT_BODY="$COMMENT_BODY""$LINK"$'\n'
done
gh issue comment $PR_NUMBER --body "$COMMENT_BODY"
echo "COMMENTED"
fi
- name: Open GH Issue on apply failure
if: ${{ always() && contains(inputs.command, 'apply') && steps.get_job_run_results.outputs.failed_steps != null }}
env:
GH_TOKEN: ${{ inputs.infra_live_token }}
JOB_ID: ${{ steps.jobs_data.outputs.JOB_ID }}
JOB_URL: ${{ steps.jobs_data.outputs.JOB_URL }}
WORKING_DIR: ${{ inputs.working_directory }}
ACTOR: ${{ inputs.actor }}
PR_NUMBER: ${{ steps.get_pr_number.outputs.get_pr_number }}
shell: bash
run: |
ISSUE_BODY=":x: Terragrunt Apply failed for "$'`'"$WORKING_DIR"$'` when applying changes requested in PR #$PR_NUMBER.\n\n _Inspect the attached logs, triage the issue, then create a new pull request with the required changes._\n\n[View logs]('"$JOB_URL"$')'
gh issue create --title "Terragrunt Apply failed for $WORKING_DIR" --body "$ISSUE_BODY" --assignee $ACTOR