Skip to content

Commit 2be98f6

Browse files
author
Nilesh Gadgi
authored
feat: Feautured tf-drift workflow (#15)
1 parent 7debe12 commit 2be98f6

File tree

1 file changed

+213
-0
lines changed

1 file changed

+213
-0
lines changed

.github/workflows/tf-drift.yml

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
name: 'Terraform Configuration Drift Detection'
2+
3+
on:
4+
schedule:
5+
- cron: '30 13 * * *' # runs every afternoon at 1:30 pm (Depends on Timezone)
6+
workflow_call:
7+
inputs:
8+
working_directory:
9+
description: 'Root directory of the terraform where all resources exist.'
10+
required: true
11+
type: string
12+
default: _example
13+
provider:
14+
description: 'Cloud provider to run the workflow. e.g. azurerm or aws'
15+
required: true
16+
type: string
17+
default: azurerm
18+
19+
#Special permissions required for OIDC authentication
20+
permissions:
21+
id-token: write
22+
contents: read
23+
issues: write
24+
25+
#These environment variables are used by the terraform azure provider to setup OIDD authenticate.
26+
env:
27+
ARM_CLIENT_ID: "${{ secrets.AZURE_CLIENT_ID }}"
28+
ARM_SUBSCRIPTION_ID: "${{ secrets.AZURE_SUBSCRIPTION_ID }}"
29+
ARM_TENANT_ID: "${{ secrets.AZURE_TENANT_ID }}"
30+
31+
jobs:
32+
terraform-plan:
33+
name: 'Terraform Plan'
34+
runs-on: ubuntu-latest
35+
env:
36+
#this is needed since we are running terraform with read-only permissions
37+
ARM_SKIP_PROVIDER_REGISTRATION: true
38+
outputs:
39+
tfplanExitCode: ${{ steps.tf-plan.outputs.exitcode }}
40+
41+
steps:
42+
# Checkout the repository to the GitHub Actions runner
43+
- name: Checkout
44+
uses: actions/checkout@v3
45+
46+
# install AWS-cli
47+
- name: Install AWS CLI
48+
if: ${{ inputs.provider == 'aws' }}
49+
uses: aws-actions/configure-aws-credentials@v1
50+
with:
51+
aws-access-key-id: ${{ secrets.aws_access_key_id }}
52+
aws-secret-access-key: ${{ secrets.aws_secret_access_key }}
53+
aws-region: us-east-2
54+
55+
# Install azure-cli
56+
- name: Install Azure CLI
57+
if: ${{ inputs.provider == 'azurerm' }}
58+
uses: azure/login@v1
59+
with:
60+
creds: ${{ secrets.AZURE_CREDENTIALS }}
61+
62+
# Install the latest version of the Terraform CLI
63+
- name: Setup Terraform
64+
uses: hashicorp/setup-terraform@v2
65+
with:
66+
terraform_wrapper: false
67+
68+
# Run some scripts
69+
- name: Run shell commands
70+
run: ls -la
71+
72+
# Initialize a new or existing Terraform working directory by creating initial files, loading any remote state, downloading modules, etc.
73+
- name: "Terraform Init"
74+
uses: hashicorp/terraform-github-actions@master
75+
with:
76+
tf_actions_subcommand: "init"
77+
tf_actions_version: 1.3.6
78+
tf_actions_working_dir: ${{ inputs.working_directory }}
79+
env:
80+
GITHUB_TOKEN: '${{ secrets.GITHUB }}'
81+
82+
# Generates an execution plan for Terraform
83+
# An exit code of 0 indicated no changes, 1 a terraform failure, 2 there are pending changes.
84+
- name: Terraform Plan
85+
id: tf-plan
86+
run: |
87+
export exitcode=0
88+
cd ${{ inputs.working_directory }}
89+
terraform plan -detailed-exitcode -no-color -out tfplan || export exitcode=$?
90+
91+
echo "exitcode=$exitcode" >> $GITHUB_OUTPUT
92+
93+
if [ $exitcode -eq 1 ]; then
94+
echo Terraform Plan Failed!
95+
exit 1
96+
else
97+
exit 0
98+
fi
99+
100+
# Save plan to artifacts
101+
- name: Publish Terraform Plan
102+
uses: actions/upload-artifact@v3
103+
with:
104+
name: tfplan
105+
path: tfplan
106+
107+
# Create string output of Terraform Plan
108+
- name: Create String Output
109+
id: tf-plan-string
110+
run: |
111+
cd ${{ inputs.working_directory }}
112+
TERRAFORM_PLAN=$(terraform show -no-color tfplan)
113+
114+
delimiter="$(openssl rand -hex 8)"
115+
echo "summary<<${delimiter}" >> $GITHUB_OUTPUT
116+
echo "## Terraform Plan Output" >> $GITHUB_OUTPUT
117+
echo "<details><summary>Click to expand</summary>" >> $GITHUB_OUTPUT
118+
echo "" >> $GITHUB_OUTPUT
119+
echo '```terraform' >> $GITHUB_OUTPUT
120+
echo "$TERRAFORM_PLAN" >> $GITHUB_OUTPUT
121+
echo '```' >> $GITHUB_OUTPUT
122+
echo "</details>" >> $GITHUB_OUTPUT
123+
echo "${delimiter}" >> $GITHUB_OUTPUT
124+
125+
# Publish Terraform Plan as task summary
126+
- name: Publish Terraform Plan to Task Summary
127+
env:
128+
SUMMARY: ${{ steps.tf-plan-string.outputs.summary }}
129+
run: |
130+
echo "$SUMMARY" >> $GITHUB_STEP_SUMMARY
131+
132+
# If changes are detected, create a new issue
133+
- name: Publish Drift Report
134+
if: steps.tf-plan.outputs.exitcode == 2
135+
uses: actions/github-script@v6
136+
env:
137+
SUMMARY: "${{ steps.tf-plan-string.outputs.summary }}"
138+
with:
139+
github-token: ${{ secrets.GITHUB }}
140+
script: |
141+
const body = `${process.env.SUMMARY}`;
142+
const title = 'Terraform Configuration Drift Detected';
143+
const creator = 'github-actions[bot]'
144+
145+
// Look to see if there is an existing drift issue
146+
const issues = await github.rest.issues.listForRepo({
147+
owner: context.repo.owner,
148+
repo: context.repo.repo,
149+
state: 'open',
150+
creator: creator,
151+
title: title
152+
})
153+
154+
if( issues.data.length > 0 ) {
155+
// We assume there shouldn't be more than 1 open issue, since we update any issue we find
156+
const issue = issues.data[0]
157+
158+
if ( issue.body == body ) {
159+
console.log('Drift Detected: Found matching issue with duplicate content')
160+
} else {
161+
console.log('Drift Detected: Found matching issue, updating body')
162+
github.rest.issues.update({
163+
owner: context.repo.owner,
164+
repo: context.repo.repo,
165+
issue_number: issue.number,
166+
body: body
167+
})
168+
}
169+
} else {
170+
console.log('Drift Detected: Creating new issue')
171+
172+
github.rest.issues.create({
173+
owner: context.repo.owner,
174+
repo: context.repo.repo,
175+
title: title,
176+
body: body
177+
})
178+
}
179+
180+
# If changes aren't detected, close any open drift issues
181+
- name: Publish Drift Report
182+
if: steps.tf-plan.outputs.exitcode == 0
183+
uses: actions/github-script@v6
184+
with:
185+
github-token: ${{ secrets.GITHUB_TOKEN }}
186+
script: |
187+
const title = 'Terraform Configuration Drift Detected';
188+
const creator = 'github-actions[bot]'
189+
190+
// Look to see if there is an existing drift issue
191+
const issues = await github.rest.issues.listForRepo({
192+
owner: context.repo.owner,
193+
repo: context.repo.repo,
194+
state: 'open',
195+
creator: creator,
196+
title: title
197+
})
198+
199+
if( issues.data.length > 0 ) {
200+
const issue = issues.data[0]
201+
202+
github.rest.issues.update({
203+
owner: context.repo.owner,
204+
repo: context.repo.repo,
205+
issue_number: issue.number,
206+
state: 'closed'
207+
})
208+
}
209+
210+
# Mark the workflow as failed if drift detected
211+
- name: Error on Failure
212+
if: steps.tf-plan.outputs.exitcode == 2
213+
run: exit 1

0 commit comments

Comments
 (0)