6
6
# checked against the workspace, and if $override=true and there were
7
7
# no hard-mandatory violations of Sentinel policies, does an apply.
8
8
# If an apply is done, the script waits for it to finish and then
9
- # downloads the apply log and the before and after state files .
9
+ # downloads the apply log and the state file .
10
10
11
11
# Make sure TFE_TOKEN and TFE_ORG environment variables are set
12
12
# to owners team token and organization name for the respective
@@ -188,47 +188,57 @@ EOF
188
188
sed " s/placeholder/${workspace} /" < workspace.template.json > workspace.json
189
189
190
190
# Check to see if the workspace already exists
191
+ echo " "
191
192
echo " Checking to see if workspace exists"
192
193
check_workspace_result=$( curl -s --header " Authorization: Bearer $TFE_TOKEN " --header " Content-Type: application/vnd.api+json" " https://${address} /api/v2/organizations/${organization} /workspaces/${workspace} " )
193
194
194
195
# Parse workspace_id from check_workspace_result
195
196
workspace_id=$( echo $check_workspace_result | python -c " import sys, json; print(json.load(sys.stdin)['data']['id'])" )
197
+ echo " "
196
198
echo " Workspace ID: " $workspace_id
197
199
198
200
# Create workspace if it does not already exist
199
201
if [ -z " $workspace_id " ]; then
202
+ echo " "
200
203
echo " Workspace did not already exist; will create it."
201
204
workspace_result=$( curl -s --header " Authorization: Bearer $TFE_TOKEN " --header " Content-Type: application/vnd.api+json" --request POST --data @workspace.json " https://${address} /api/v2/organizations/${organization} /workspaces" )
202
205
203
206
# Parse workspace_id from workspace_result
204
207
workspace_id=$( echo $workspace_result | python -c " import sys, json; print(json.load(sys.stdin)['data']['id'])" )
208
+ echo " "
205
209
echo " Workspace ID: " $workspace_id
206
210
else
211
+ echo " "
207
212
echo " Workspace already existed."
208
213
fi
209
214
210
215
# Create configuration version
216
+ echo " "
211
217
echo " Creating configuration version."
212
218
configuration_version_result=$( curl -s --header " Authorization: Bearer $TFE_TOKEN " --header " Content-Type: application/vnd.api+json" --data @configversion.json " https://${address} /api/v2/workspaces/${workspace_id} /configuration-versions" )
213
219
214
220
# Parse configuration_version_id and upload_url
215
221
config_version_id=$( echo $configuration_version_result | python -c " import sys, json; print(json.load(sys.stdin)['data']['id'])" )
216
222
upload_url=$( echo $configuration_version_result | python -c " import sys, json; print(json.load(sys.stdin)['data']['attributes']['upload-url'])" )
223
+ echo " "
217
224
echo " Config Version ID: " $config_version_id
218
225
echo " Upload URL: " $upload_url
219
226
220
227
# Upload configuration
228
+ echo " "
221
229
echo " Uploading configuration version using ${config_dir} .tar.gz"
222
230
# curl -s --request PUT -F '[email protected] ' "$upload_url"
223
231
curl -s --header " Content-Type: application/octet-stream" --request PUT --data-binary @${config_dir} .tar.gz " $upload_url "
224
232
225
233
# Check if a variables.csv file is in the configuration directory
226
234
# If so, use it. Otherwise, use the one in the current directory.
227
235
if [ -f " ${config_dir} /variables.csv" ]; then
236
+ echo " "
228
237
echo " Found variables.csv in ${config_dir} ."
229
238
echo " Will load variables from it."
230
239
variables_file=${config_dir} /variables.csv
231
240
else
241
+ echo " "
232
242
echo " Did not find variables.csv in configuration."
233
243
echo " Will load variables from ./variables.csv"
234
244
variables_file=variables.csv
@@ -243,6 +253,7 @@ escape_string()
243
253
sedDelim=$( printf ' \001' )
244
254
245
255
# Add variables to workspace
256
+ echo " "
246
257
while IFS=' ,' read -r key value category hcl sensitive
247
258
do
248
259
fixedkey=$( escape_string " $key " )
@@ -255,6 +266,7 @@ done < ${variables_file}
255
266
# List Sentinel Policies
256
267
sentinel_list_result=$( curl -s --header " Authorization: Bearer $TFE_TOKEN " --header " Content-Type: application/vnd.api+json" " https://${address} /api/v2/organizations/${organization} /policies" )
257
268
sentinel_policy_count=$( echo $sentinel_list_result | python -c " import sys, json; print(json.load(sys.stdin)['meta']['pagination']['total-count'])" )
269
+ echo " "
258
270
echo " Number of Sentinel policies: " $sentinel_policy_count
259
271
260
272
# Do a run
@@ -263,13 +275,15 @@ run_result=$(curl -s --header "Authorization: Bearer $TFE_TOKEN" --header "Conte
263
275
264
276
# Parse run_result
265
277
run_id=$( echo $run_result | python -c " import sys, json; print(json.load(sys.stdin)['data']['id'])" )
278
+ echo " "
266
279
echo " Run ID: " $run_id
267
280
268
281
# Check run result in loop
269
282
continue=1
270
283
while [ $continue -ne 0 ]; do
271
284
# Sleep
272
285
sleep $sleep_duration
286
+ echo " "
273
287
echo " Checking run status"
274
288
275
289
# Check the status of run
@@ -295,18 +309,21 @@ while [ $continue -ne 0 ]; do
295
309
# exist or are applicable to the workspace
296
310
if [[ " $run_status " == " planned" ]] && [[ " $is_confirmable " == " True" ]] && [[ " $override " == " no" ]]; then
297
311
continue=0
312
+ echo " "
298
313
echo " There are " $sentinel_policy_count " policies, but none of them are applicable to this workspace."
299
314
echo " Check the run in Terraform Enterprise UI and apply there if desired."
300
315
save_plan=" true"
301
316
# cost_estimated means plan finished and costs were estimated
302
317
# exist or are applicable to the workspace
303
318
elif [[ " $run_status " == " cost_estimated" ]] && [[ " $is_confirmable " == " True" ]] && [[ " $override " == " no" ]]; then
304
319
continue=0
320
+ echo " "
305
321
echo " There are " $sentinel_policy_count " policies, but none of them are applicable to this workspace."
306
322
echo " Check the run in Terraform Enterprise UI and apply there if desired."
307
323
save_plan=" true"
308
324
elif [[ " $run_status " == " planned" ]] && [[ " $is_confirmable " == " True" ]] && [[ " $override " == " yes" ]]; then
309
325
continue=0
326
+ echo " "
310
327
echo " There are " $sentinel_policy_count " policies, but none of them are applicable to this workspace."
311
328
echo " Since override was set to \" yes\" , we are applying."
312
329
# Do the apply
@@ -315,6 +332,7 @@ while [ $continue -ne 0 ]; do
315
332
applied=" true"
316
333
elif [[ " $run_status " == " cost_estimated" ]] && [[ " $is_confirmable " == " True" ]] && [[ " $override " == " yes" ]]; then
317
334
continue=0
335
+ echo " "
318
336
echo " There are " $sentinel_policy_count " policies, but none of them are applicable to this workspace."
319
337
echo " Since override was set to \" yes\" , we are applying."
320
338
# Do the apply
@@ -325,51 +343,63 @@ while [ $continue -ne 0 ]; do
325
343
elif [[ " $run_status " == " policy_checked" ]]; then
326
344
continue=0
327
345
# Do the apply
346
+ echo " "
328
347
echo " Policies passed. Doing Apply"
329
348
apply_result=$( curl -s --header " Authorization: Bearer $TFE_TOKEN " --header " Content-Type: application/vnd.api+json" --data @apply.json https://${address} /api/v2/runs/${run_id} /actions/apply)
330
349
applied=" true"
331
350
# policy_override means at least 1 Sentinel policy failed
332
351
# but since $override is "yes", we will override and then apply
333
352
elif [[ " $run_status " == " policy_override" ]] && [[ " $override " == " yes" ]]; then
334
353
continue=0
354
+ echo " "
335
355
echo " Some policies failed, but overriding"
336
356
# Get the policy check ID
357
+ echo " "
337
358
echo " Getting policy check ID"
338
359
policy_result=$( curl -s --header " Authorization: Bearer $TFE_TOKEN " https://${address} /api/v2/runs/${run_id} /policy-checks)
339
360
# Parse out the policy check ID
340
361
policy_check_id=$( echo $policy_result | python -c " import sys, json; print(json.load(sys.stdin)['data'][0]['id'])" )
362
+ echo " "
341
363
echo " Policy Check ID: " $policy_check_id
342
364
# Override policy
365
+ echo " "
343
366
echo " Overriding policy check"
344
367
override_result=$( curl -s --header " Authorization: Bearer $TFE_TOKEN " --header " Content-Type: application/vnd.api+json" --request POST https://${address} /api/v2/policy-checks/${policy_check_id} /actions/override)
345
368
# Do the apply
369
+ echo " "
346
370
echo " Doing Apply"
347
371
apply_result=$( curl -s --header " Authorization: Bearer $TFE_TOKEN " --header " Content-Type: application/vnd.api+json" --data @apply.json https://${address} /api/v2/runs/${run_id} /actions/apply)
348
372
applied=" true"
349
373
# policy_override means at least 1 Sentinel policy failed
350
374
# but since $override is "no", we will not override
351
375
# and will not apply
352
376
elif [[ " $run_status " == " policy_override" ]] && [[ " $override " == " no" ]]; then
377
+ echo " "
353
378
echo " Some policies failed, but will not override. Check run in Terraform Enterprise UI."
354
379
save_plan=" true"
355
380
continue=0
356
381
# errored means that plan had an error or that a hard-mandatory
357
382
# policy failed
358
383
elif [[ " $run_status " == " errored" ]]; then
384
+ echo " "
359
385
echo " Plan errored or hard-mandatory policy failed"
360
386
save_plan=" true"
361
387
continue=0
362
388
elif [[ " $run_status " == " planned_and_finished" ]]; then
389
+ echo " "
363
390
echo " Plan indicates no changes to apply."
364
391
save_plan=" true"
365
392
continue=0
366
393
elif [[ " run_status" == " canceled" ]]; then
394
+ echo " "
367
395
echo " The run was canceled."
368
396
continue=0
369
397
elif [[ " run_status" == " force_canceled" ]]; then
398
+ echo " "
370
399
echo " The run was canceled forcefully."
371
400
continue=0
372
401
elif [[ " run_status" == " discarded" ]]; then
402
+ echo " "
373
403
echo " The run was discarded."
374
404
continue=0
375
405
else
@@ -380,18 +410,21 @@ done
380
410
381
411
# Get the plan log if $save_plan is true
382
412
if [[ " $save_plan " == " true" ]]; then
413
+ echo " "
383
414
echo " Getting the result of the Terraform Plan."
384
415
plan_result=$( curl -s --header " Authorization: Bearer $TFE_TOKEN " --header " Content-Type: application/vnd.api+json" https://${address} /api/v2/runs/${run_id} ? include=plan)
385
416
plan_log_url=$( echo $plan_result | python -c " import sys, json; print(json.load(sys.stdin)['included'][0]['attributes']['log-read-url'])" )
417
+ echo " "
386
418
echo " Plan Log:"
387
419
# Retrieve Plan Log from the URL
388
420
# and output to shell and file
389
421
curl -s $plan_log_url | tee ${run_id} .log
390
422
fi
391
423
392
- # Get the apply log and state files (before and after) if an apply was done
424
+ # Get the apply log and state file if an apply was done
393
425
if [[ " $applied " == " true" ]]; then
394
426
427
+ echo " "
395
428
echo " An apply was done."
396
429
echo " Will download apply log and state file."
397
430
@@ -400,13 +433,15 @@ if [[ "$applied" == "true" ]]; then
400
433
401
434
# Get apply ID
402
435
apply_id=$( echo $check_result | python -c " import sys, json; print(json.load(sys.stdin)['included'][0]['id'])" )
436
+ echo " "
403
437
echo " Apply ID:" $apply_id
404
438
405
439
# Check apply status periodically in loop
406
440
continue=1
407
441
while [ $continue -ne 0 ]; do
408
442
409
443
sleep $sleep_duration
444
+ echo " "
410
445
echo " Checking apply status"
411
446
412
447
# Check the apply status
@@ -434,29 +469,51 @@ if [[ "$applied" == "true" ]]; then
434
469
435
470
# Get apply log URL
436
471
apply_log_url=$( echo $check_result | python -c " import sys, json; print(json.load(sys.stdin)['data']['attributes']['log-read-url'])" )
472
+ echo " "
437
473
echo " Apply Log URL:"
438
474
echo " ${apply_log_url} "
439
475
440
476
# Retrieve Apply Log from the URL
441
477
# and output to shell and file
478
+ echo " "
442
479
curl -s $apply_log_url | tee ${apply_id} .log
443
480
444
481
# Get state version ID from after the apply
445
- state_id_after=$( echo $check_result | python -c " import sys, json; print(json.load(sys.stdin)['data']['relationships']['state-versions']['data'][0]['id'])" )
446
- echo " State ID:" ${state_id_after}
447
-
448
- # Call API to get information about the state version including its URL
449
- state_file_after_url_result=$( curl -s --header " Authorization: Bearer $TFE_TOKEN " https://${address} /api/v2/state-versions/${state_id_after} )
482
+ state_id=$( echo $check_result | python -c " import sys, json; print(json.load(sys.stdin)['data']['relationships']['state-versions']['data'][0]['id'])" )
483
+ echo " "
484
+ echo " State ID:" ${state_id}
485
+
486
+ # Call API to get information about the state version including its URL and outputs
487
+ state_file_url_result=$( curl -s --header " Authorization: Bearer $TFE_TOKEN " " https://${address} /api/v2/state-versions/${state_id} ?include=outputs" )
488
+
489
+ # Retrieve and echo outputs from state
490
+ # Note that we retrieved outputs in the last API call by
491
+ # adding `?include=outputs`
492
+ # Instead of doing that, we could have retrieved the state version output
493
+ # IDs from the relationships of the above API call and could have then
494
+ # called the State Version Output API to retrieve details for each output.
495
+ # That would have involved URLs like
496
+ # "https://${address}/api/v2/state-version-outputs/${output_id}"
497
+ # See `https://www.terraform.io/docs/cloud/api/state-version-outputs.html#show-a-state-version-output`
498
+ num_outputs=$( echo $state_file_url_result | python -c " import sys, json; print(len(json.load(sys.stdin)['included']))" )
499
+ echo " "
500
+ echo " Outputs from State:"
501
+ for (( output= 0 ;output< $num_outputs ;output++ ))
502
+ do
503
+ echo $state_file_url_result | python -c " import sys, json; print(json.dumps(json.load(sys.stdin)['included'][$output ]['attributes'], sort_keys=True))"
504
+ done
450
505
451
506
# Get state file URL from the result
452
- state_file_after_url=$( echo $state_file_after_url_result | python -c " import sys, json; print(json.load(sys.stdin)['data']['attributes']['hosted-state-download-url'])" )
507
+ state_file_url=$( echo $state_file_url_result | python -c " import sys, json; print(json.load(sys.stdin)['data']['attributes']['hosted-state-download-url'])" )
508
+ echo " "
453
509
echo " URL for state file after apply:"
454
- echo ${state_file_after_url }
510
+ echo ${state_file_url }
455
511
456
512
# Retrieve state file from the URL
457
513
# and output to shell and file
514
+ echo " "
458
515
echo " State file after the apply:"
459
- curl -s $state_file_after_url | tee ${apply_id} -after.tfstate
516
+ curl -s $state_file_url | tee ${apply_id} -after.tfstate
460
517
461
518
fi
462
519
0 commit comments