-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Lena Sadowska
authored and
Lena Sadowska
committed
Oct 19, 2023
1 parent
5b77ac6
commit 8d2cce3
Showing
1 changed file
with
289 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,289 @@ | ||
## Plan only workflow for testing purposes. Allows working directory | ||
# distinction to test with different modules. | ||
|
||
name: Terraform Validate and Plan | ||
on: | ||
workflow_call: | ||
secrets: | ||
infracost-api-key: | ||
description: 'The API key for infracost' | ||
required: false | ||
inputs: | ||
aws-account-id: | ||
description: 'The AWS account ID to deploy to' | ||
required: true | ||
type: number | ||
aws-role-name-readonly: | ||
default: '${{ github.event.repository.name }}-ro' | ||
description: 'The Read Only AWS role to assume for PR branch executions' | ||
required: false | ||
type: string | ||
aws-role-name-readwrite: | ||
default: '${{ github.event.repository.name }}-rw' | ||
description: 'The Read/Write AWS role to assume for main branch executions' | ||
required: false | ||
type: string | ||
aws-region: | ||
default: 'eu-west-2' | ||
description: 'The AWS region to deploy to' | ||
required: false | ||
type: string | ||
enable-infracost: | ||
default: false | ||
description: 'Whether to run infracost on the Terraform Plan (secrets.infracost-api-key must be set if enabled)' | ||
required: false | ||
type: boolean | ||
runs-on: | ||
default: "ubuntu-latest" | ||
description: 'Single label value for the GitHub runner to use (custom value only applies to Terraform Plan and Apply steps)' | ||
required: false | ||
type: string | ||
terraform-log-level: | ||
default: '' | ||
description: 'The log level of terraform' | ||
required: false | ||
type: string | ||
terraform-state-key: | ||
default: '${{ github.event.repository.name }}.tfstate' | ||
description: 'The key of the terraform state' | ||
required: false | ||
type: string | ||
terraform-values-file: | ||
default: 'values/production.tfvars' | ||
description: 'The values file to use' | ||
required: false | ||
type: string | ||
terraform-version: | ||
default: '1.5.7' | ||
description: 'The version of terraform to use' | ||
required: false | ||
type: string | ||
working-directory: | ||
default: '.' | ||
description: 'Working directory' | ||
required: false | ||
type: string | ||
|
||
|
||
env: | ||
TF_LOG: ${{ inputs.terraform-log-level }} | ||
AWS_WEB_IDENTITY_TOKEN_FILE: /tmp/web_identity_token_file | ||
|
||
permissions: | ||
id-token: write | ||
contents: read | ||
pull-requests: write | ||
|
||
jobs: | ||
terraform-format: | ||
name: "Terraform Format" | ||
runs-on: ubuntu-latest | ||
defaults: | ||
run: | ||
working-directory: ${{ inputs.working-directory }} | ||
outputs: | ||
result: ${{ steps.format.outcome }} | ||
steps: | ||
- name: Checkout Repository | ||
uses: actions/checkout@v3 | ||
- name: Setup Terraform | ||
uses: hashicorp/setup-terraform@v2 | ||
with: | ||
terraform_version: ${{ inputs.terraform-version }} | ||
- name: Terraform Format | ||
id: format | ||
uses: dflook/terraform-fmt-check@v1 | ||
terraform-lint: | ||
name: "Terraform Lint" | ||
runs-on: ubuntu-latest | ||
defaults: | ||
run: | ||
working-directory: ${{ inputs.working-directory }} | ||
outputs: | ||
result: ${{ steps.lint.outcome }} | ||
steps: | ||
- name: Checkout Repository | ||
uses: actions/checkout@v3 | ||
- name: Setup Linter | ||
uses: terraform-linters/setup-tflint@v3 | ||
- name: Linter Initialize | ||
run: tflint --init | ||
- name: Linting Code | ||
id: lint | ||
run: tflint -f compact | ||
terraform-plan: | ||
name: "Terraform Plan" | ||
if: github.event_name == 'pull_request' || (github.event_name == 'push' && github.ref == 'refs/heads/main') | ||
runs-on: ${{ inputs.runs-on }} | ||
defaults: | ||
run: | ||
working-directory: ${{ inputs.working-directory }} | ||
outputs: | ||
result-auth: ${{ steps.auth.outcome }} | ||
result-init: ${{ steps.init.outcome }} | ||
result-validate: ${{ steps.validate.outcome }} | ||
result-s3-backend-check: ${{ steps.s3-backend-check.outcome }} | ||
result-plan: ${{ steps.plan.outcome }} | ||
plan-stdout: ${{ steps.plan.outputs.stdout }} | ||
steps: | ||
- name: Checkout Repository | ||
uses: actions/checkout@v3 | ||
- name: Setup node | ||
uses: actions/setup-node@v3 | ||
with: | ||
node-version: 16 | ||
- name: Setup Terraform | ||
uses: hashicorp/setup-terraform@v2 | ||
with: | ||
terraform_version: ${{ inputs.terraform-version }} | ||
- name: Retrieve Web Identity Token for AWS Authentication | ||
run: | | ||
curl -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" "$ACTIONS_ID_TOKEN_REQUEST_URL&audience=sts.amazonaws.com" | jq -r '.value' > $AWS_WEB_IDENTITY_TOKEN_FILE | ||
- name: Determine AWS Role | ||
id: role | ||
run: | | ||
if [[ "${GITHUB_REF##*/}" == "main" ]]; then | ||
echo "name=${{ inputs.aws-role-name-readwrite }}" >> $GITHUB_OUTPUT | ||
else | ||
echo "name=${{ inputs.aws-role-name-readonly }}" >> $GITHUB_OUTPUT | ||
fi | ||
- name: Authenticate with AWS | ||
id: auth | ||
uses: aws-actions/configure-aws-credentials@v2 | ||
with: | ||
aws-region: ${{ inputs.aws-region }} | ||
role-session-name: ${{ github.event.repository.name }} | ||
role-to-assume: arn:aws:iam::${{ inputs.aws-account-id }}:role/${{ steps.role.outputs.name }} | ||
mask-aws-account-id: 'no' | ||
- name: Terraform Init | ||
id: init | ||
run: terraform init -backend-config="bucket=${{ inputs.aws-account-id }}-${{ inputs.aws-region }}-tfstate" -backend-config="key=${{ inputs.terraform-state-key }}" -backend-config="encrypt=true" -backend-config="dynamodb_table=${{ inputs.aws-account-id }}-${{ inputs.aws-region }}-tflock" -backend-config="region=${{ inputs.aws-region }}" | ||
- name: Terraform Validate | ||
id: validate | ||
run: terraform validate -no-color | ||
- name: Terraform S3 Backend Check | ||
id: s3-backend-check | ||
run: | | ||
if grep -E '^[^#]*backend\s+"s3"' terraform.tf; then | ||
echo "Terraform configuration references an S3 backend." | ||
else | ||
echo "Terraform configuration does not reference an S3 backend." | ||
exit 1 | ||
fi | ||
- name: Terraform Plan | ||
id: plan | ||
run: | | ||
terraform plan -var-file=${{ inputs.terraform-values-file }} -no-color -input=false -out=tfplan | ||
- name: Terraform Plan JSON Output | ||
run: | | ||
terraform show -json tfplan > tfplan.json | ||
- name: Upload tfplan | ||
uses: actions/upload-artifact@v3 | ||
with: | ||
name: tfplan | ||
path: "tfplan*" | ||
retention-days: 1 | ||
get-cost-estimate: | ||
name: "Get Cost Estimate" | ||
if: github.event_name == 'pull_request' && inputs.enable-infracost | ||
runs-on: ubuntu-latest | ||
defaults: | ||
run: | ||
working-directory: ${{ inputs.working-directory }} | ||
needs: | ||
- terraform-plan | ||
steps: | ||
- name: Setup Infracost | ||
uses: infracost/actions/setup@v2 | ||
with: | ||
api-key: ${{ secrets.infracost-api-key }} | ||
currency: GBP | ||
- name: Download tfplan | ||
uses: actions/download-artifact@v3 | ||
with: | ||
name: tfplan | ||
- name: Generate Infracost Cost Estimate | ||
run: | | ||
infracost breakdown --path=tfplan.json \ | ||
--format=json \ | ||
--out-file=/tmp/infracost.json | ||
- name: Post Infracost comment | ||
run: | | ||
infracost comment github --path=/tmp/infracost.json \ | ||
--repo=$GITHUB_REPOSITORY \ | ||
--github-token=${{github.token}} \ | ||
--pull-request=${{github.event.pull_request.number}} \ | ||
--behavior=update | ||
update-pr: | ||
name: "Update PR" | ||
if: github.event_name == 'pull_request' && (success() || failure()) | ||
runs-on: ubuntu-latest | ||
needs: | ||
- terraform-format | ||
- terraform-lint | ||
- terraform-plan | ||
steps: | ||
- name: Add PR Comment | ||
uses: actions/github-script@v6 | ||
env: | ||
PLAN: "${{ needs.terraform-plan.outputs.plan-stdout }}" | ||
with: | ||
script: | | ||
// 1. Retrieve existing bot comments for the PR | ||
const { data: comments } = await github.rest.issues.listComments({ | ||
owner: context.repo.owner, | ||
repo: context.repo.repo, | ||
issue_number: context.issue.number, | ||
}) | ||
const botComment = comments.find(comment => { | ||
return comment.user.type === 'Bot' && comment.body.includes('Pull Request Review Status') | ||
}) | ||
// 2. Check output length | ||
const PLAN = process.env.PLAN || ''; | ||
const excludedStrings = ["Refreshing state...", "Reading...", "Read complete after"]; | ||
const filteredLines = PLAN.split('\n').filter(line => | ||
!excludedStrings.some(excludedStr => line.includes(excludedStr)) | ||
); | ||
var planOutput = filteredLines.join('\n').trim(); | ||
if (planOutput.length < 1 || planOutput.length > 65000) { | ||
planOutput = "Terraform Plan output is too large, please view the workflow run logs directly." | ||
} | ||
// 3. Prepare format of the comment | ||
const output = `### Pull Request Review Status | ||
* π <b>Terraform Format and Style:</b> \`${{ needs.terraform-format.outputs.result }}\` | ||
* π <b>Terraform Linting:</b> \`${{ needs.terraform-lint.outputs.result }}\` | ||
* π <b>AWS Authentication:</b> \`${{ needs.terraform-plan.outputs.result-auth }}\` | ||
* π§ <b>Terraform Initialisation:</b> \`${{ needs.terraform-plan.outputs.result-init }}\` | ||
* π€ <b>Terraform Validation:</b> \`${{ needs.terraform-plan.outputs.result-validate }}\` | ||
* π <b>Terraform S3 Backend:</b> \`${{ needs.terraform-plan.outputs.result-s3-backend-check }}\` | ||
* π <b>Terraform Plan:</b> \`${{ needs.terraform-plan.outputs.result-plan }}\` | ||
<details><summary><b>Output: π Terraform Plan</b></summary> | ||
\`\`\` | ||
${planOutput} | ||
\`\`\` | ||
</details> | ||
*<b>Pusher:</b> @${{ github.actor }}, <b>Action:</b> \`${{ github.event_name }}\`* | ||
*<b>Workflow Run Link:</b> ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}*`; | ||
// 4. If we have a comment, update it, otherwise create a new one | ||
if (botComment) { | ||
github.rest.issues.updateComment({ | ||
owner: context.repo.owner, | ||
repo: context.repo.repo, | ||
comment_id: botComment.id, | ||
body: output | ||
}) | ||
} else { | ||
github.rest.issues.createComment({ | ||
issue_number: context.issue.number, | ||
owner: context.repo.owner, | ||
repo: context.repo.repo, | ||
body: output | ||
}) | ||
} |