-
Notifications
You must be signed in to change notification settings - Fork 0
359 lines (330 loc) Β· 12.7 KB
/
terraform-plan-and-apply-azure.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
name: Terraform Validate, Plan & Apply for Azure
on:
workflow_call:
secrets:
infracost-api-key:
description: 'The API key for infracost'
required: false
GH_TOKEN:
description: 'The GitHub Token'
required: true
inputs:
AZURE_CLIENT_ID:
description: 'The Azure Client ID'
required: true
type: string
AZURE_TENANT_ID:
description: 'The Azure Tenant ID'
required: true
type: string
terraform-subscription-id:
description: 'The azure subscription ID to deploy to'
required: true
type: number
# terraform-backend-subscription-id:
# description: 'Azure subscription ID for the backend storage account'
# type: string
# required: true
terraform-state-key:
default: '${{ github.event.repository.name }}.tfstate'
description: 'The key of the terraform state'
required: false
type: string
terraform-backend-storage-account-name:
description: 'The name of the state storage account'
required: true
type: string
terraform-backend-container-name:
description: 'The name of the state storage account container'
required: false
type: string
description: 'tfstate'
state-resource-group-name:
type: string
required: false
description: 'The name of the state resource group'
default: 'rg-alz-state'
azure-primary-region:
type: string
required: false
description: 'The primary Azure region to deploy to'
default: 'uksouth'
environment:
type: string
required: true
description: 'The environment to deploy to'
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
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
terraform-log-level:
default: ''
description: 'The log level of terraform'
required: false
type: string
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
env:
TF_LOG: ${{ inputs.terraform-log-level }}
permissions:
id-token: write
contents: read
pull-requests: write
jobs:
terraform-format:
name: "Terraform Format"
runs-on: ubuntu-latest
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
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 }}
environment: ${{ inputs.environment }}
env:
ARM_CLIENT_ID: ${{ inputs.AZURE_CLIENT_ID }}
ARM_SUBSCRIPTION_ID: ${{ inputs.terraform-subscription-id }}
ARM_TENANT_ID: ${{ inputs.AZURE_TENANT_ID }}
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 }}
- uses: de-vri-es/setup-git-credentials@v2
with:
credentials: https://whoisit:${{ secrets.GH_TOKEN }}@github.com
- name: Login via Az module
uses: azure/login@v1
with:
client-id: ${{ inputs.AZURE_CLIENT_ID }}
tenant-id: ${{ inputs.AZURE_TENANT_ID }}
subscription-id: ${{ inputs.terraform-subscription-id }}
- name: Run Terraform init
run: |
terraform init -var use_oidc=true -var state_resource_group_name=${{ inputs.state-resource-group-name }} -lock=false \
-backend-config="subscription_id=${{ inputs.terraform-backend-subscription-id }}" \
-backend-config="storage_account_name=${{ inputs.terraform-backend-storage-account-name }}" \
-backend-config="container_name=${{ inputs.terraform-backend-container-name }}" \
-backend-config="key=${{ inputs.terraform-state-key }}" \
-backend-config="tenant_id=${{ inputs.AZURE_TENANT_ID }}" \
-backend-config="client_id=${{ inputs.AZURE_CLIENT_ID }}"
working-directory: ${{ inputs.terraform-working-directory }}
- name: Terraform Validate
id: validate
run: terraform validate -no-color
# - name: Terraform Storage Account Backend Check
# id: storage-account-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
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>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
})
}
terraform-apply:
name: "Terraform Apply"
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
runs-on: ${{ inputs.runs-on }}
environment: ${{ inputs.environment }}
env:
ARM_CLIENT_ID: ${{ inputs.AZURE_CLIENT_ID }}
ARM_SUBSCRIPTION_ID: ${{ inputs.terraform-subscription-id }}
ARM_TENANT_ID: ${{ inputs.AZURE_TENANT_ID }}
needs:
- terraform-format
- terraform-lint
- terraform-plan
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 }}
- uses: de-vri-es/setup-git-credentials@v2
with:
credentials: https://whoisit:${{ secrets.GH_TOKEN }}@github.com
- name: Login via Az module
uses: azure/login@v1
with:
client-id: ${{ inputs.AZURE_CLIENT_ID }}
tenant-id: ${{ inputs.AZURE_TENANT_ID }}
subscription-id: ${{ inputs.terraform-subscription-id }}
- name: Run Terraform init
working-directory: ${{ inputs.terraform-working-directory }}
run: |
terraform init -lock=false \
-backend-config="subscription_id=${{ inputs.terraform-backend-subscription-id }}" \
-backend-config="storage_account_name=${{ inputs.terraform-backend-storage-account-name }}" \
-backend-config="container_name=${{ inputs.terraform-backend-container-name }}" \
-backend-config="key=${{ inputs.terraform-state-key }}" \
-backend-config="tenant_id=${{ inputs.AZURE_TENANT_ID }}" \
-backend-config="client_id=${{ inputs.AZURE_CLIENT_ID }}"
- name: Download tfplan
uses: actions/download-artifact@v3
with:
name: tfplan
- name: Terraform Apply
run: terraform apply -auto-approve -input=false tfplan