Skip to content

feat: Added replace tokens #5

feat: Added replace tokens

feat: Added replace tokens #5

---
run-name: 'Terraform workflow'
on:
workflow_call:
inputs:
replace_tokens:
required: true
type: boolean
default: true
description: 'Set true to replace tokens in the terraform files.'
environmentName:
required: false
type: string
default: "dev"
terraform_version:
required: false
type: string
default: "1.8.0"
region:
required: false
type: string
default: "canadaeast"
working_directory:
required: true
type: string
default: "terraform/common"
description: 'Root directory of the terraform where all resources exist.'
resourceName:
required: false
type: string
default: "moduletestref"
backend_rg_name:
required: false
type: string
default: "terraform-state-rg"
backend_sa_name:
required: false
type: string
default: "tfstatestorageaccountest"
backend_container_name:
required: false
type: string
default: "terraform-state"
provider:
required: true
type: string
description: 'Cloud provider to run the workflow. e.g. azurerm, aws, gcp or digitalocean'
aws_region:
required: false
type: string
default: us-east-2
description: 'AWS region of terraform deployment.'
gcp_region:
required: false
type: string
description: 'GCP region of terraform deployment.'
var_file:
required: false
type: string
description: 'Terraform var file directory. e.g. vars/dev.tfvars'
destroy:
required: false
type: boolean
default: false
description: 'Set true to to destroy terraform infrastructure.'
approvers:
required: false
type: string
description: 'Approvals list to approve apply or destroy'
terraform_version:
type: string
default: 1.3.6
description: 'Required erraform version '
timeout:
required: false
type: number
default: 10
description: 'Timeout for approval step'
minimum-approvals:
required: false
type: string
default: 1
description: 'Minimum approvals required to accept the plan'
token_format:
required: false
type: string
default: access_token
description: 'Output format for the generated authentication token. For OAuth 2.0 access tokens, specify "access_token". For OIDC tokens, specify "id_token". To skip token generation, leave this value empty'
access_token_lifetime:
required: false
type: string
default: 300s
description: 'Desired lifetime duration of the access token, in seconds'
project_id:
required: false
type: string
description: 'ID of the default project to use for future API calls and invocations.'
create_credentials_file:
required: false
type: string
default: true
description: 'If true, the action will securely generate a credentials file which can be used for authentication via gcloud and Google Cloud SDKs.'
secrets:
AZURE_CREDENTIALS:
required: false
description: 'Azure Credentials to install Azure in github runner.'
AZURE_CLIENT_ID:
required: false
description: 'Client ID for service principal in Azure.'
AZURE_SUBSCRIPTION_ID:
required: false
description: 'Subscription ID in Azure.'
AZURE_TENANT_ID:
required: false
description: 'Tenant ID in Azure.'
AZURE_CLIENT_SECRET:
required: false
description: 'Client Secret for the Azure app registration.'
AWS_ACCESS_KEY_ID:
required: false
description: 'AWS Access Key ID to install AWS CLI.'
BUILD_ROLE:
required: false
description: 'AWS OIDC role for aws authentication.'
AWS_SECRET_ACCESS_KEY:
required: false
description: 'AWS Secret access key to install AWS CLI'
AWS_SESSION_TOKEN:
required: false
description: 'AWS Session Token to install AWS CLI'
GCP_CREDENTIALS:
required: false
description: 'The Google Cloud JSON service account key to use for authentication'
DIGITALOCEAN_ACCESS_TOKEN:
required: false
description: 'The DigitalOcean Personal Access Token for Application & API'
env-vars:
required: false
description: 'Pass required environment variables'
WORKLOAD_IDENTITY_PROVIDER:
required: false
description: 'The full identifier of the Workload Identity Provider'
SERVICE_ACCOUNT:
required: false
description: 'The service account to be used'
jobs:
terraform-workflow:
runs-on: ubuntu-latest
outputs:
tfplanExitCode: ${{ steps.tf-plan.outputs.exitcode }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Update environment variables
if: ${{ inputs.replace_tokens == true }}
uses: cschleiden/replace-tokens@v1
with:
tokenPrefix: '#{'
tokenSuffix: '}#'
files: '["${{ inputs.working_directory }}/main.tf", "${{ inputs.working_directory }}/backend.tf"]'
env:
RESOURCE_NAME: ${{ inputs.resourceName }}
SUBSCRIPTION_ID: "${{ secrets.AZURE_SUBSCRIPTION_ID }}"
ENV_NAME: ${{ inputs.environmentName }}
REPLACE_REGION: ${{ inputs.region }}
CLIENT_ID: "${{ secrets.AZURE_CLIENT_ID }}"
CLIENT_SECRET: "${{ secrets.AZURE_CLIENT_SECRET }}"
TENANT_ID: "${{ secrets.AZURE_TENANT_ID }}"
BACKEND_RG_NAME: ${{ inputs.backend_rg_name }}
BACKEND_SA_NAME: ${{ inputs.backend_sa_name }}
BACKEND_CONTAINER_NAME: ${{ inputs.backend_container_name }}
- name: Set environment variables
run: |
(
cat <<'_EOT'
${{ secrets.env-vars }}
_EOT
) >> "$GITHUB_ENV"
- name: Install AWS CLI
if: ${{ inputs.provider == 'aws' }}
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-session-token: ${{ secrets.AWS_SESSION_TOKEN }}
role-to-assume: ${{ secrets.BUILD_ROLE }}
aws-region: ${{ inputs.aws_region }}
role-duration-seconds: 900
role-skip-session-tagging: true
- name: Install Azure CLI
if: ${{ inputs.provider == 'azurerm' }}
uses: azure/login@v2
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- name: 'Authenticate to Google Cloud'
if: ${{ inputs.provider == 'gcp' }}
uses: 'google-github-actions/auth@v2'
with:
credentials_json: '${{ secrets.GCP_CREDENTIALS }}'
create_credentials_file: ${{ inputs.create_credentials_file }}
token_format: ${{ inputs.token_format }}
workload_identity_provider: ${{ secrets.WORKLOAD_IDENTITY_PROVIDER }}
service_account: ${{ secrets.SERVICE_ACCOUNT }}
access_token_lifetime: ${{ inputs.access_token_lifetime }}
project_id: ${{ inputs.project_id }}
- name: Install doctl
if: ${{ inputs.provider == 'digitalocean' }}
uses: digitalocean/action-doctl@v2
with:
token: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }}
- name: Set up Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: ${{ inputs.terraform_version }}
- name: 'Terraform Format'
if: ${{ inputs.destroy != true }}
id: fmt
uses: 'dflook/terraform-fmt-check@v1'
with:
actions_subcommand: 'fmt'
path: ${{ inputs.working_directory }}
- name: terraform init
run: |
export ARM_CLIENT_ID="${{ secrets.AZURE_CLIENT_ID }}"
export ARM_CLIENT_SECRET="${{ secrets.AZURE_CLIENT_SECRET }}"
export ARM_TENANT_ID="${{ secrets.AZURE_TENANT_ID }}"
export ARM_SUBSCRIPTION_ID="${{ secrets.AZURE_SUBSCRIPTION_ID }}"
cd ${{ inputs.working_directory }}
terraform init
- name: 'Terraform validate'
if: ${{ inputs.destroy != true }}
id: validate
uses: dflook/terraform-validate@v1
with:
path: ${{ inputs.working_directory }}
- name: Terraform Plan
id: tf-plan
run: |
export exitcode=0
cd ${{ inputs.working_directory }}
export ARM_CLIENT_ID="${{ secrets.AZURE_CLIENT_ID }}"
export ARM_CLIENT_SECRET="${{ secrets.AZURE_CLIENT_SECRET }}"
export ARM_TENANT_ID="${{ secrets.AZURE_TENANT_ID }}"
export ARM_SUBSCRIPTION_ID="${{ secrets.AZURE_SUBSCRIPTION_ID }}"
if [ "${{ inputs.destroy }}" = true ]; then
if [ -n "${{ inputs.var_file }}" ]; then
terraform plan -destroy -out tfplan --var-file=${{ inputs.var_file }}
else
terraform plan -destroy -out tfplan
fi
else
if [ -n "${{ inputs.var_file }}" ]; then
terraform plan -out tfplan --var-file=${{ inputs.var_file }}
else
terraform plan -out tfplan
fi
fi
- name: Publish Terraform Plan
uses: actions/upload-artifact@v4
with:
name: tfplan
path: ${{ inputs.working_directory }}/tfplan
- name: Create String Output
id: tf-plan-string
run: |
cd ${{ inputs.working_directory }}
TERRAFORM_PLAN=$(terraform show -no-color tfplan)
delimiter="$(openssl rand -hex 8)"
echo "summary<<${delimiter}" >> $GITHUB_OUTPUT
echo "## Terraform Plan Output" >> $GITHUB_OUTPUT
echo "<details><summary>Click to expand</summary>" >> $GITHUB_OUTPUT
echo "" >> $GITHUB_OUTPUT
echo '```terraform' >> $GITHUB_OUTPUT
echo "$TERRAFORM_PLAN" >> $GITHUB_OUTPUT
echo '```' >> $GITHUB_OUTPUT
echo "</details>" >> $GITHUB_OUTPUT
echo "${delimiter}" >> $GITHUB_OUTPUT
- name: "Accept plan or deny"
uses: trstringer/manual-approval@v1
timeout-minutes: ${{ inputs.timeout }}
with:
secret: ${{ github.TOKEN }}
approvers: ${{ inputs.approvers }}
minimum-approvals: ${{ inputs.minimum-approvals }}
issue-title: "Terraform Plan for Infrastructure Update"
- name: terraform apply
if: ${{ inputs.destroy != true }}
run: |
export ARM_CLIENT_ID="${{ secrets.AZURE_CLIENT_ID }}"
export ARM_CLIENT_SECRET="${{ secrets.AZURE_CLIENT_SECRET }}"
export ARM_TENANT_ID="${{ secrets.AZURE_TENANT_ID }}"
export ARM_SUBSCRIPTION_ID="${{ secrets.AZURE_SUBSCRIPTION_ID }}"
if [ -n "${{ inputs.var_file }}" ]; then
cd ${{ inputs.working_directory }}
terraform apply -var-file="${{ inputs.var_file }}" -auto-approve
else
cd ${{ inputs.working_directory }}
terraform apply -auto-approve
fi
- name: Find Errored Terraform State
if: ${{ always() }}
run: |
cd ${{ inputs.working_directory }}
if [ -f "errored.tfstate" ]; then
ls -la errored.tfstate
echo "Uploading errored.tfstate as artifact..."
else
echo "Errored Terraform state file not found."
fi
- name: Upload Errored Terraform State Artifact
if: ${{ always() }} && success() && steps.find_errored_tfstate.outputs['errored_found'] == 'true'
uses: actions/upload-artifact@v4
with:
name: errored_tfstate
path: ${{ inputs.working_directory }}/errored.tfstate
- name: Terraform destroy
if: ${{ inputs.destroy == true }}
id: destroy
run: |
export ARM_CLIENT_ID="${{ secrets.AZURE_CLIENT_ID }}"
export ARM_CLIENT_SECRET="${{ secrets.AZURE_CLIENT_SECRET }}"
export ARM_TENANT_ID="${{ secrets.AZURE_TENANT_ID }}"
export ARM_SUBSCRIPTION_ID="${{ secrets.AZURE_SUBSCRIPTION_ID }}"
if [ -n "${{ inputs.var_file }}" ]; then
cd ${{ inputs.working_directory }}
terraform destroy -var-file="${{ inputs.var_file }}" -auto-approve
else
cd ${{ inputs.working_directory }}
terraform destroy -auto-approve
fi
...