Skip to content

uses format

uses format #14

Workflow file for this run

name: Pipelines
run-name: Run Gruntwork Pipelines
on:
workflow_call:
inputs:
# This field can be overriden to customize the runner used for pipelines
# workflows.
#
# IMPORTANT: To use self-hosted runners this workflow must be hosted in
# the same GitHub organization as your infra-live repository.
# See https://docs.github.com/en/actions/using-workflows/reusing-workflows#using-self-hosted-runners
#
# The value must be an escaped JSON string that will be decoded to the
# jobs.runs-on field
# See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idruns-on
#
# For example:
# - A simple github runner: "\"ubuntu-22.04\""
# - A list of labels: "[\"self-hosted\", \"linux\"]"
# - A map: "{group: \"ubuntu-runners\", labels: \"ubuntu-20.04-16core\"}"
runner:
type: string
default: '"ubuntu-latest"'
secrets:
PIPELINES_READ_TOKEN:
required: true
INFRA_ROOT_WRITE_TOKEN:
required: true
ORG_REPO_ADMIN_TOKEN:
required: false
env:
PIPELINES_CLI_VERSION: v0.29.0-rc1
PIPELINES_ACTIONS_VERSION: 2024-08-27_gruntcon_githubapp
BOILERPLATE_VERSION: v0.5.16
GRUNTWORK_INSTALLER_VERSION: v0.0.40
# Disables all executions of terragrunt. This is useful for debugging
# specifics of pipelines actions/workflows and bypassing the (usually time consuming)
# actual IaC execution.
SKIP_TERRAGRUNT: false
# GitHub Actions tends to hit resource exhaustion and kill running jobs
# if we leave parallelism unbounded, so we set the max to 10 for a sane default.
TERRAGRUNT_PARALLELISM: 10
jobs:
pipelines_orchestrate:
name: Detect Infrastructure Changes
runs-on: ${{ fromJSON(inputs.runner) }}
steps:
- name: Fetch Gruntwork Read Token
id: fetch-piplines-gruntwork-read-token
uses: gruntwork-test/pipelines-credentials
with:
PIPELINES_TOKEN_PATH: "pipelines-read/gruntwork-io"
FALLBACK_TOKEN: ${{ secrets.PIPELINES_READ_TOKEN }}
- name: Fetch Org Read Token
id: fetch-piplines-org-read-token
uses: gruntwork-test/pipelines-credentials
with:
PIPELINES_TOKEN_PATH: "pipelines-read/${{ env.GITHUB_REPOSITORY_OWNER }}"
FALLBACK_TOKEN: ${{ secrets.PIPELINES_READ_TOKEN }}
- name: Checkout Pipelines Actions
id: checkout_actions
uses: actions/checkout@v4
with:
path: pipelines-actions
repository: gruntwork-io/pipelines-actions
ref: ${{ env.PIPELINES_ACTIONS_VERSION }}
token: ${{ steps.fetch-pipelines-gruntwork-read-token.PIPELINES_TOKEN }}
- name: Validate PIPELINES_READ_TOKEN
if: always() && steps.checkout_actions.conclusion != 'success'
env:
GH_TOKEN: ${{ github.token }}
GITHUB_ORG: ${{ github.repository }}
PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
shell: bash
run: |
logs_url="https://github.com/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID"
msg=$(printf "<h2>❌ Plan for $PR_HEAD_SHA</h2>❌ Gruntwork Pipelines was unable to checkout the <code>pipelines-actions</code> repository. Please ensure the <code>PIPELINES_READ_TOKEN</code> is valid and unexpired. <a href=\"https://docs.gruntwork.io/pipelines/security/machine-users#ci-read-only-user\">Learn More</a><br><br><br><a href=\"$logs_url\">View full logs</a>")
echo "::error:: $msg"
echo "$msg" >> "$GITHUB_STEP_SUMMARY"
pull_number=$(jq --raw-output .pull_request.number "$GITHUB_EVENT_PATH")
gh pr comment $pull_number -b "$msg" -R $GITHUB_ORG || true # || true incase this fails on a non-PR run
- name: Check out repo code
uses: actions/checkout@v4
with:
path: infra-live-repo
fetch-depth: 0
- name: Preflight Checks
uses: ./pipelines-actions/.github/actions/pipelines-preflight-action
with:
IS_ROOT: "true"
PIPELINES_READ_TOKEN: ${{ secrets.PIPELINES_READ_TOKEN }}
INFRA_ROOT_WRITE_TOKEN: ${{ secrets.INFRA_ROOT_WRITE_TOKEN }}
ORG_REPO_ADMIN_TOKEN: ${{ secrets.ORG_REPO_ADMIN_TOKEN }}
- name: Pipelines Orchestrate
id: orchestrate
uses: ./pipelines-actions/.github/actions/pipelines-orchestrate
with:
token: ${{ secrets.PIPELINES_READ_TOKEN }}
outputs:
pipelines_jobs: ${{ steps.orchestrate.outputs.jobs }}
pipelines_execute:
name: ${{ contains(matrix.jobs.Action.Command, 'plan') && 'Plan' || 'Apply' }} - ${{ matrix.jobs.ChangeType }} - ${{ matrix.jobs.WorkingDirectory }}
needs: [pipelines_orchestrate]
runs-on: ${{ fromJSON(inputs.runner) }}
# GHA can't check for length, so we just check if there is an item in the 0 index
if: fromJson(needs.pipelines_orchestrate.outputs.pipelines_jobs)[0] != null
strategy:
fail-fast: false
matrix:
jobs: ${{ fromJson(needs.pipelines_orchestrate.outputs.pipelines_jobs) }}
steps:
- name: Checkout Pipelines Actions
uses: actions/checkout@v4
with:
path: pipelines-actions
repository: gruntwork-io/pipelines-actions
ref: ${{ env.PIPELINES_ACTIONS_VERSION }}
token: ${{ secrets.PIPELINES_READ_TOKEN }}
- name: Check out repo code
uses: actions/checkout@v4
with:
path: infra-live-repo
fetch-depth: 0
- name: Bootstrap Workflow
id: gruntwork_context
uses: ./pipelines-actions/.github/actions/pipelines-bootstrap
with:
token: ${{ secrets.PIPELINES_READ_TOKEN }}
change_type: ${{ matrix.jobs.ChangeType }}
branch: ${{ matrix.jobs.Ref }}
working_directory: ${{ matrix.jobs.WorkingDirectory }}
account_id: ${{ matrix.jobs.AccountId }}
terragrunt_command: ${{ matrix.jobs.Action.Command }} ${{ matrix.jobs.Action.Args }}
additional_data: ${{ toJson(matrix.jobs.AdditionalData) }}
child_account_id: ${{ matrix.jobs.AdditionalData.ChildAccountId }}
account_names: ${{ matrix.jobs.AdditionalData.AccountNames }}
# TODO: This should be "first_new_account_name".
new_account_name: ${{ matrix.jobs.NewAccounts[0].Name }}
# To learn more about customizing Pipelines see our documentation at https://docs.gruntwork.io/pipelines/maintain/extending/
- name: "[Baseline]: Pre Provision New Account Custom Action"
uses: ./pipelines-actions/.github/custom-actions/pre-provision-new-account
if: ${{ steps.gruntwork_context.outputs.action == 'PROVISION_ACCOUNT' }}
with:
PIPELINES_READ_TOKEN: ${{ secrets.PIPELINES_READ_TOKEN }}
INFRA_ROOT_WRITE_TOKEN: ${{ secrets.INFRA_ROOT_WRITE_TOKEN }}
gruntwork_context: ${{ toJson(steps.gruntwork_context.outputs) }}
- name: "[ProvisionAccount]: Provision New Account"
id: provision_new_account
if: ${{ steps.gruntwork_context.outputs.action == 'PROVISION_ACCOUNT' }}
uses: ./pipelines-actions/.github/actions/pipelines-provision-account-action
with:
PIPELINES_READ_TOKEN: ${{ secrets.PIPELINES_READ_TOKEN }}
INFRA_ROOT_WRITE_TOKEN: ${{ secrets.INFRA_ROOT_WRITE_TOKEN }}
gruntwork_context: ${{ toJson(steps.gruntwork_context.outputs) }}
- name: "[Baseline]: Post Provision New Account Custom Action"
uses: ./pipelines-actions/.github/custom-actions/post-provision-new-account
if: ${{ steps.gruntwork_context.outputs.action == 'PROVISION_ACCOUNT' }}
with:
PIPELINES_READ_TOKEN: ${{ secrets.PIPELINES_READ_TOKEN }}
INFRA_ROOT_WRITE_TOKEN: ${{ secrets.INFRA_ROOT_WRITE_TOKEN }}
gruntwork_context: ${{ toJson(steps.gruntwork_context.outputs) }}
baseline_pull_request_url: ${{ steps.provision_new_account.outputs.pull_request_url }}
- name: "[Baseline]: Pre Baseline Core Account Action"
uses: ./pipelines-actions/.github/custom-actions/pre-baseline-core-accounts
if: steps.gruntwork_context.outputs.action == 'BASELINE_ACCOUNT'
with:
PIPELINES_READ_TOKEN: ${{ secrets.PIPELINES_READ_TOKEN }}
gruntwork_context: ${{ toJson(steps.gruntwork_context.outputs) }}
# Run the core accounts baselines(shared, logs, security, etc. to ensure the account is setup correctly)
- name: "Run core accounts baselines"
id: core_accounts_baselines
if: steps.gruntwork_context.outputs.action == 'BASELINE_ACCOUNT'
# TODO: Rename this as pipelines-apply-core-baselines or something similar
uses: ./pipelines-actions/.github/actions/pipelines-baseline-account-action
with:
PIPELINES_READ_TOKEN: ${{ secrets.PIPELINES_READ_TOKEN }}
gruntwork_context: ${{ toJson(steps.gruntwork_context.outputs) }}
- name: "[Baseline]: Post Baseline Core Account Action"
uses: ./pipelines-actions/.github/custom-actions/post-baseline-core-accounts
if: steps.gruntwork_context.outputs.action == 'BASELINE_ACCOUNT'
with:
PIPELINES_READ_TOKEN: ${{ secrets.PIPELINES_READ_TOKEN }}
gruntwork_context: ${{ toJson(steps.gruntwork_context.outputs) }}
- name: "[TerragruntExecute]: Authenticate with AWS and then Invoke Terragrunt"
id: terragrunt
if: ${{ steps.gruntwork_context.outputs.action == 'TERRAGRUNT_EXECUTE' }}
uses: ./pipelines-actions/.github/actions/pipelines-execute
env:
TERRAGRUNT_AUTH_PROVIDER_CMD: "pipelines auth terragrunt-credentials --ci github-actions --cloud aws --wd ."
with:
token: ${{ secrets.PIPELINES_READ_TOKEN }}
tf_binary: ${{ steps.gruntwork_context.outputs.tf_binary }}
working_directory: ${{ steps.gruntwork_context.outputs.working_directory }}
terragrunt_command: ${{ steps.gruntwork_context.outputs.terragrunt_command }}
infra_live_repo_branch: ${{ steps.gruntwork_context.outputs.branch }}
gruntwork_config_file: ${{ steps.gruntwork_context.outputs.gruntwork_config_file }}
infra_live_repo: "."
infra_live_directory: "."
deploy_branch_name: ${{ steps.gruntwork_context.outputs.deploy_branch_name }}
- name: Update comment
uses: ./pipelines-actions/.github/actions/pipelines-status-update
if: always()
with:
step_name: ${{ matrix.jobs.ChangeType }}
step_working_directory: ${{ matrix.jobs.WorkingDirectory }}
step_status: ${{ (steps.provision_new_account.conclusion == 'success' || steps.terragrunt.conclusion == 'success' || steps.core_accounts_baselines.conclusion == 'success') && 'success' || 'failed' }}
step_details: ${{ steps.terragrunt.outputs.formatted_plan_output }}
step_details_extended_log: ${{ steps.terragrunt.outputs.execute_stdout_log }}
pull_request_number: ${{ steps.gruntwork_context.outputs.pr_number }}
outputs:
account_id: ${{ matrix.jobs.AccountId }}
branch: ${{ steps.gruntwork_context.outputs.branch }}
action: ${{ steps.gruntwork_context.outputs.action }}
working_directory: ${{ steps.gruntwork_context.outputs.working_directory }}
terragrunt_command: ${{ steps.gruntwork_context.outputs.terragrunt_command }}
additional_data: ${{ steps.gruntwork_context.outputs.additional_data }}
child_account_id: ${{ steps.gruntwork_context.outputs.child_account_id }}
pr_number: ${{ steps.gruntwork_context.outputs.pr_number }}
delegate_management: ${{ steps.gruntwork_context.outputs.delegate_management }}
pipelines_apply_baselines:
name: Baseline Child Account ${{ contains(fromJson(needs.pipelines_orchestrate.outputs.pipelines_jobs)[0].Action.Command, 'plan') && 'Plan' || 'Apply' }} - ${{ matrix.jobs.Name }} (${{ matrix.jobs.ID }})
needs: [pipelines_orchestrate, pipelines_execute]
runs-on: ${{ fromJSON(inputs.runner) }}
# GHA can't check for length, so we just check if there is an item in the 0 index
if: fromJson(needs.pipelines_orchestrate.outputs.pipelines_jobs)[0].NewAccounts[0] != null
strategy:
fail-fast: false
matrix:
jobs: ${{ fromJson(needs.pipelines_orchestrate.outputs.pipelines_jobs)[0].NewAccounts }}
steps:
- name: Checkout Pipelines Actions
uses: actions/checkout@v4
with:
path: pipelines-actions
repository: gruntwork-io/pipelines-actions
ref: ${{ env.PIPELINES_ACTIONS_VERSION }}
token: ${{ secrets.PIPELINES_READ_TOKEN }}
- name: Check out repo code
uses: actions/checkout@v4
with:
path: infra-live-repo
fetch-depth: 0
- name: Update comment
uses: ./pipelines-actions/.github/actions/pipelines-status-update
with:
step_name: Baseline Child Account ${{ matrix.jobs.Name }}
step_status: "in_progress"
pull_request_number: ${{ needs.pipelines_execute.outputs.pr_number }}
- name: Bootstrap Workflow
id: gruntwork_context
uses: ./pipelines-actions/.github/actions/pipelines-bootstrap
with:
token: ${{ secrets.PIPELINES_READ_TOKEN }}
change_type: ${{ fromJson(needs.pipelines_orchestrate.outputs.pipelines_jobs)[0].ChangeType }}
branch: ${{ fromJson(needs.pipelines_orchestrate.outputs.pipelines_jobs)[0].Ref }}
working_directory: ${{ fromJson(needs.pipelines_orchestrate.outputs.pipelines_jobs)[0].WorkingDirectory }}
account_id: ${{ fromJson(needs.pipelines_orchestrate.outputs.pipelines_jobs)[0].AccountId }}
terragrunt_command: ${{ fromJson(needs.pipelines_orchestrate.outputs.pipelines_jobs)[0].Action.Command }} ${{ fromJson(needs.pipelines_orchestrate.outputs.pipelines_jobs)[0].Action.Args }}
additional_data: ${{ toJson(fromJson(needs.pipelines_orchestrate.outputs.pipelines_jobs)[0].AdditionalData) }}
account_names: ${{ fromJson(needs.pipelines_orchestrate.outputs.pipelines_jobs)[0].AdditionalData.AccountNames }}
child_account_id: ${{ matrix.jobs.ID }}
new_account_name: ${{ matrix.jobs.Name }}
# To learn more about customizing Pipelines see our documentation at https://docs.gruntwork.io/pipelines/maintain/extending/
- name: "[Baseline]: Pre Baseline Child Account Action"
uses: ./pipelines-actions/.github/custom-actions/pre-baseline-child-account
with:
PIPELINES_READ_TOKEN: ${{ secrets.PIPELINES_READ_TOKEN }}
account_id: ${{ matrix.jobs.ID }}
account_name: ${{ matrix.jobs.Name }}
job: ${{ toJson(fromJson(needs.pipelines_orchestrate.outputs.pipelines_jobs)[0]) }}
gruntwork_context: ${{ toJson(steps.gruntwork_context.outputs) }}
- name: "[Baseline]: Baseline the Child Account"
id: baseline_child_account
uses: ./pipelines-actions/.github/actions/pipelines-baseline-child-account-action
with:
PIPELINES_READ_TOKEN: ${{ secrets.PIPELINES_READ_TOKEN }}
account_id: ${{ matrix.jobs.ID }}
account_name: ${{ matrix.jobs.Name }}
job: ${{ toJson(fromJson(needs.pipelines_orchestrate.outputs.pipelines_jobs)[0]) }}
gruntwork_context: ${{ toJson(steps.gruntwork_context.outputs) }}
- name: "[Baseline]: Post Baseline Child Account Action"
uses: ./pipelines-actions/.github/custom-actions/post-baseline-child-account
with:
PIPELINES_READ_TOKEN: ${{ secrets.PIPELINES_READ_TOKEN }}
account_id: ${{ matrix.jobs.ID }}
account_name: ${{ matrix.jobs.Name }}
job: ${{ toJson(fromJson(needs.pipelines_orchestrate.outputs.pipelines_jobs)[0]) }}
gruntwork_context: ${{ toJson(steps.gruntwork_context.outputs) }}
- name: Update comment
uses: ./pipelines-actions/.github/actions/pipelines-status-update
if: always()
with:
step_name: Baseline Child Account ${{ matrix.jobs.Name }}
step_status: ${{ steps.baseline_child_account.conclusion == 'success' && 'success' || 'failed' }}
step_details: ${{ steps.baseline_child_account.outputs.formatted_plan_output || 'Check the logs for more details.' }}
step_details_extended_log: ${{ steps.baseline_child_account.outputs.execute_stdout_log }}
pull_request_number: ${{ needs.pipelines_execute.outputs.pr_number }}
pipelines_setup_delegated_repo:
name: "Setup Delegated Repo"
needs: [pipelines_orchestrate, pipelines_apply_baselines, pipelines_execute]
runs-on: ${{ fromJSON(inputs.runner) }}
# GHA can't check for length, so we just check if there is an item in the 0 index
if: ${{ fromJson(needs.pipelines_orchestrate.outputs.pipelines_jobs)[0].NewAccounts[0] != null && needs.pipelines_execute.outputs.delegate_management == 'true' && needs.pipelines_execute.outputs.terragrunt_command == 'run-all apply' }}
steps:
- name: Checkout Pipelines Actions
uses: actions/checkout@v4
with:
path: pipelines-actions
repository: gruntwork-io/pipelines-actions
ref: ${{ env.PIPELINES_ACTIONS_VERSION }}
token: ${{ secrets.PIPELINES_READ_TOKEN }}
- name: Check out repo code
uses: actions/checkout@v4
with:
path: infra-live-repo
fetch-depth: 0
- name: Bootstrap Workflow
id: gruntwork_context
uses: ./pipelines-actions/.github/actions/pipelines-bootstrap
with:
token: ${{ secrets.PIPELINES_READ_TOKEN }}
change_type: ${{ fromJson(needs.pipelines_orchestrate.outputs.pipelines_jobs)[0].ChangeType }}
branch: ${{ fromJson(needs.pipelines_orchestrate.outputs.pipelines_jobs)[0].Ref }}
working_directory: ${{ fromJson(needs.pipelines_orchestrate.outputs.pipelines_jobs)[0].WorkingDirectory }}
account_id: ${{ fromJson(needs.pipelines_orchestrate.outputs.pipelines_jobs)[0].AccountId }}
terragrunt_command: ${{ fromJson(needs.pipelines_orchestrate.outputs.pipelines_jobs)[0].Action.Command }} ${{ needs.pipelines_orchestrate.outputs.pipelines_jobs[0].Action.Args }}
additional_data: ${{ toJson(fromJson(needs.pipelines_orchestrate.outputs.pipelines_jobs)[0].AdditionalData) }}
child_account_id: ${{ fromJson(needs.pipelines_orchestrate.outputs.pipelines_jobs)[0].AdditionalData.ChildAccountId }}
account_names: ${{ fromJson(needs.pipelines_orchestrate.outputs.pipelines_jobs)[0].AdditionalData.AccountNames }}
# This is just to help bootstrap find one of the (possibly several) new account request files
# Inside those files is some shared config that we need to setup access control such as
# the delegated_repo_name (which is the same in all the new request files)
new_account_name: ${{ fromJson(needs.pipelines_orchestrate.outputs.pipelines_jobs)[0].NewAccounts[0].Name }}
- name: "Create Access Control PR"
id: access_control_pr
uses: ./pipelines-actions/.github/actions/pipelines-provision-access-control-action
with:
gruntwork_context: ${{ toJson(steps.gruntwork_context.outputs) }}
PIPELINES_READ_TOKEN: ${{ secrets.PIPELINES_READ_TOKEN }}
ORG_REPO_ADMIN_TOKEN: ${{ secrets.ORG_REPO_ADMIN_TOKEN }}
- name: "Create and bootstrap delegated Repo"
id: provision_delegated_repo
uses: ./pipelines-actions/.github/actions/pipelines-provision-repo-action
with:
gruntwork_context: ${{ toJson(steps.gruntwork_context.outputs) }}
access_control_pull_request_url: ${{ steps.access_control_pr.outputs.pull_request_url }}
PIPELINES_READ_TOKEN: ${{ secrets.PIPELINES_READ_TOKEN }}
ORG_REPO_ADMIN_TOKEN: ${{ secrets.ORG_REPO_ADMIN_TOKEN }}
# To learn more about customizing Pipelines see our documentation at https://docs.gruntwork.io/pipelines/maintain/extending/
- name: "Post create delegated repo custom actions"
uses: ./pipelines-actions/.github/custom-actions/post-create-delegated-repo
with:
gruntwork_context: ${{ toJson(steps.gruntwork_context.outputs) }}
access_control_pull_request_url: ${{ steps.access_control_pr.outputs.pull_request_url }}
PIPELINES_READ_TOKEN: ${{ secrets.PIPELINES_READ_TOKEN }}
ORG_REPO_ADMIN_TOKEN: ${{ secrets.ORG_REPO_ADMIN_TOKEN }}
delegated_repo_pull_request_url: ${{ steps.provision_delegated_repo.outputs.pull_request_url }}