Skip to content

Commit 136d643

Browse files
committed
added automation-script
1 parent d82c3cd commit 136d643

File tree

8 files changed

+232
-0
lines changed

8 files changed

+232
-0
lines changed
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# TFE Automation Script
2+
Script to automate interactions with Terraform Enterprise, including the creation of a workspace, uploading of Terraform code, setting of a variable, and triggering of plan and apply.
3+
4+
## Introduction
5+
This script uses curl to interact with Terraform Enterprise via the Terraform Enterprise REST API. The same APIs could be used from Jenkins or other solutions to incorporate Terraform Enterprise into your CI/CD pipeline.
6+
7+
The script does the following steps:
8+
1. Packages main.tf into the myconfig.tar.gz file.
9+
1. Creates the workspace.
10+
1. Creates a new configuration version.
11+
1. Uploads the myconfig.tar.gz file as a new configuration. (This last step triggers an initial run which will error because we have not yet set the name variable in the workspace. That is OK.)
12+
1. Adds a variable called "name" to the workspace and sets it to the name you pass into the script as the first argument
13+
1. Starts a new run.
14+
1. Enters a loop to check the run results periodically.
15+
- If $run_status is "policy_checked", it does an Apply. In this case, all Sentinel policies passed.
16+
- If $run_status is "policy_override" and $override is "yes", it overrides the failed policy checks and does an Apply. In this case, one or more Sentinel policies failed, but they were marked "advisory" or "soft-mandatory" and the script was configured to override the failure.
17+
- If $run_status is "policy_override" and $override is "no", it prints out a message indicating that some policies failed and are not being overridden.
18+
- If $run_status is "errored", either the plan failed or a Sentinel policy marked "hard-mandatory" failed. The script terminates.
19+
20+
Note that some json template files are included from which other json files are generated so that they can be passed to the curl commands.
21+
22+
In addition to the loadAndRunWorkspace.sh script, this example includes the following files:
23+
24+
1. config/main.tf: the file with some Terraform code that says "Hello" to the person whose name is given and generates a random number.
25+
1. workspace.template.json which is used to generate workspace.json which is used when creating the workspace.
26+
1. configversion.json which is used to generate a new configuration version.
27+
1. variable.template.json which is used to generate variable.json which is used when creating a variable called "name" in the workspace.
28+
1. run.template.json which is used to generate run.json which is used when triggering a run against the workspace.
29+
1. apply.json which is used when doing the apply against the workspace.
30+
31+
## Preparation
32+
Do the following before using this script:
33+
34+
1. `git clone https://github.com/hashicorp/terraform-guides.git`
35+
1. `cd operations/automation-script`
36+
37+
## Instructions
38+
Follow these instructions to run the script:
39+
40+
1. Run `./loadAndRunWorkspace.sh <name>` or `./loadAndRunWorkspace.sh <name> <override>` where \<name\> is any name (without spaces) and \<override\> is "yes" or "no". If you do not specify a value for \<override\>, the script will set it to "no".
41+
42+
### Examples
43+
`./loadAndRunWorkspace Peter` (no override will be done)
44+
`./loadAndRunWorkspace Paul yes` (override will be done)
45+
`./loadAndRunWorkspace Mary no` (no override will be done)
46+
47+
## Cleaning Up
48+
If you want to run the script again, delete the workspace from the Settings tab of the workspace in the TFE UI. You do not need to delete or touch any of the files in the directory containing the script and other files.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"comment": "apply via API"}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
variable "name" {
2+
}
3+
4+
resource "random_id" "random" {
5+
keepers {
6+
uuid = "${uuid()}"
7+
}
8+
byte_length = 32
9+
}
10+
11+
output "random" {
12+
value = "${random_id.random.hex}"
13+
}
14+
15+
output "hello_world" {
16+
value = "Hello, ${var.name}"
17+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"data": {
3+
"type": "configuration-versions"
4+
}
5+
}
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
#!/bin/bash
2+
3+
# Make sure ATLAS_TOKEN environment variable is set
4+
# to owners team token for organization
5+
6+
# Set PTFE address, organization, and workspace to create. You should edit these before running.
7+
address="roger-ptfe.hashidemos.io"
8+
organization="Solutions-Engineering"
9+
workspace="workspace-from-api"
10+
11+
# You can change sleep duration if desired
12+
sleep_duration=15
13+
14+
# name of person to set name variable to
15+
name=$1
16+
17+
# Override soft-mandatory policy checks that fail
18+
# Set to "yes" or "no"
19+
# if not specified, then we set to "no"
20+
if [ ! -z $2 ]; then
21+
override=$2
22+
else
23+
override="no"
24+
fi
25+
26+
# build myconfig.tar.gz
27+
cd config
28+
tar -cvf myconfig.tar .
29+
gzip myconfig.tar
30+
mv myconfig.tar.gz ../.
31+
cd ..
32+
33+
#Set name of workspace in workspace.json
34+
sed "s/placeholder/$workspace/" < workspace.template.json > workspace.json
35+
36+
workspace_result=$(curl --header "Authorization: Bearer $ATLAS_TOKEN" --header "Content-Type: application/vnd.api+json" --request POST --data @workspace.json "https://${address}/api/v2/organizations/${organization}/workspaces")
37+
38+
# Parse workspace_id from workspace_result
39+
workspace_id=$(echo $workspace_result | python3 -c "import sys, json; print(json.load(sys.stdin)['data']['id'])")
40+
41+
echo "Workspace ID: " $workspace_id
42+
43+
# Create configuration versions
44+
configuration_version_result=$(curl --header "Authorization: Bearer $ATLAS_TOKEN" --header "Content-Type: application/vnd.api+json" --data @configversion.json "https://${address}/api/v2/workspaces/${workspace_id}/configuration-versions")
45+
46+
# Parse configuration_version_id and upload_url
47+
config_version_id=$(echo $configuration_version_result | python3 -c "import sys, json; print(json.load(sys.stdin)['data']['id'])")
48+
upload_url=$(echo $configuration_version_result | python3 -c "import sys, json; print(json.load(sys.stdin)['data']['attributes']['upload-url'])")
49+
50+
echo "Config Version ID: " $config_version_id
51+
echo "Upload URL: " $upload_url
52+
53+
# Upload configuration
54+
curl --request PUT -F '[email protected]' "$upload_url"
55+
56+
# Add name variable
57+
sed -e "s/my-name/$name/" -e "s/my-organization/$organization/" -e "s/my-workspace/$workspace/" < variable.template.json > variable.json
58+
59+
upload_variable_result=$(curl --header "Authorization: Bearer $ATLAS_TOKEN" --header "Content-Type: application/vnd.api+json" --data @variable.json "https://${address}/api/v2/vars?filter%5Borganization%5D%5Busername%5D=${organization}&filter%5Bworkspace%5D%5Bname%5D=${workspace}")
60+
61+
# Do a run
62+
sed "s/workspace_id/$workspace_id/" < run.template.json > run.json
63+
64+
run_result=$(curl --header "Authorization: Bearer $ATLAS_TOKEN" --header "Content-Type: application/vnd.api+json" --data @run.json https://${address}/api/v2/runs)
65+
66+
# Parse run run_result
67+
run_id=$(echo $run_result | python3 -c "import sys, json; print(json.load(sys.stdin)['data']['id'])")
68+
echo "Run ID: " $run_id
69+
70+
# Check run run result
71+
continue=1
72+
while [ $continue -ne 0 ]; do
73+
# Sleep a bit
74+
sleep $sleep_duration
75+
echo "Checking run status"
76+
77+
# Check the status
78+
check_result=$(curl --header "Authorization: Bearer $ATLAS_TOKEN" --header "Content-Type: application/vnd.api+json" https://${address}/api/v2/runs/${run_id})
79+
80+
# Parse out the startus
81+
run_status=$(echo $check_result | python3 -c "import sys, json; print(json.load(sys.stdin)['data']['attributes']['status'])")
82+
echo "Run Status: " $run_status
83+
84+
# If status is "policy_checked" or "policy_override",
85+
# then do Apply. If "errored", exit loop.
86+
# Anything else, continue loop
87+
if [[ "$run_status" == "policy_checked" ]] ; then
88+
continue=0
89+
# Do the apply
90+
echo "Policies passed. Doing Apply"
91+
apply_result=$(curl --header "Authorization: Bearer $ATLAS_TOKEN" --header "Content-Type: application/vnd.api+json" --data @apply.json https://${address}/api/v2/runs/${run_id}/actions/apply)
92+
elif [[ "$run_status" == "policy_override" ]] && [[ "$override" == "yes" ]]; then
93+
continue=0
94+
echo "Some policies failed, but will override"
95+
# Get the policy check ID
96+
echo "Getting policy check ID"
97+
policy_result=$(curl --header "Authorization: Bearer $ATLAS_TOKEN" https://${address}/api/v2/runs/${run_id}/policy-checks)
98+
# Parse out the policy check ID
99+
policy_check_id=$(echo $policy_result | python3 -c "import sys, json; print(json.load(sys.stdin)['data'][0]['id'])")
100+
echo "Policy Check ID: " $policy_check_id
101+
# Override policy
102+
echo "Overriding policy check"
103+
override_result=$(curl --header "Authorization: Bearer $ATLAS_TOKEN" --header "Content-Type: application/vnd.api+json" --request POST https://${address}/api/v2/policy-checks/${policy_check_id}/actions/override)
104+
# Do the apply
105+
echo "Doing Apply"
106+
apply_result=$(curl --header "Authorization: Bearer $ATLAS_TOKEN" --header "Content-Type: application/vnd.api+json" --data @apply.json https://${address}/api/v2/runs/${run_id}/actions/apply)
107+
elif [[ "$run_status" == "policy_override" ]] && [[ "$override" == "no" ]]; then
108+
echo "Some policies failed, but will not override. Check run in Terraform Enterprise UI."
109+
continue=0
110+
elif [[ "$run_status" == "errored" ]]; then
111+
echo "Plan errored or hard-mandatory policy failed"
112+
continue=0
113+
else
114+
sleep $sleep_duration
115+
fi
116+
done
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"data": {
3+
"attributes": {
4+
"is-destroy":false
5+
},
6+
"type":"runs",
7+
"relationships": {
8+
"workspace": {
9+
"data": {
10+
"type": "workspaces",
11+
"id": "workspace_id"
12+
}
13+
}
14+
}
15+
}
16+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"data": {
3+
"type":"vars",
4+
"attributes": {
5+
"key":"name",
6+
"value":"my-name",
7+
"category":"terraform",
8+
"hcl":false,
9+
"sensitive":false
10+
}
11+
},
12+
"filter": {
13+
"organization": {
14+
"username":"my-organization"
15+
},
16+
"workspace": {
17+
"name":"my-workspace"
18+
}
19+
}
20+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"data":
3+
{
4+
"attributes": {
5+
"name":"placeholder"
6+
},
7+
"type":"workspaces"
8+
}
9+
}

0 commit comments

Comments
 (0)