From 8c40364c8bc36a8033f505f5e935ad586e2a41a1 Mon Sep 17 00:00:00 2001 From: Dale Newby Date: Tue, 6 Dec 2022 11:16:08 -0500 Subject: [PATCH 1/4] Initial commit --- .gitignore | 3 ++ README.md | 34 ++++++++++++- tfc-dump.pl | 144 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 180 insertions(+), 1 deletion(-) create mode 100755 tfc-dump.pl diff --git a/.gitignore b/.gitignore index ecf66f8..b71d360 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,6 @@ inc/ /MANIFEST.bak /pm_to_blib /*.zip + +# tfc-dump creates .json files that don't belong in version control +*.json diff --git a/README.md b/README.md index e973b67..498dc14 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,34 @@ # tfc-dump -Perl command using the Terraform Cloud API to export workspaces, variables, and variable sets to JSON files. +Perl script using the Terraform Cloud API to export workspaces, variables, and variable sets to JSON files. + +## Motivation +During the review of a disaster recovery plan, we realized that we didn't have a +record of the values we set for variables in Terraform Cloud workspaces. +It would be difficult to recover from the accidental deletion of a Terraform +Cloud workspace. +This Perl script was quickly written to export information to JSON files +using the Terraform Cloud API. +The exported information includes workspaces, variables, and variable sets +which covers most of what we need. + +## How to use tfc-dump.pl +1. Install [tfc-ops](https://github.com/silinternational/tfc-ops). +2. Obtain a Terraform Cloud access token. +3. `export ATLAS\_TOKEN=_terraform-cloud-access-token_` +4. To dump one workspace: +```tfc-dump.pl --org=_terraform-cloud-organization_ --workspace=_terraform-cloud-workspace-name_``` +5. To dump all workspaces in an organization: +```tfc-dump.pl --org=_terraform-cloud-organization_ --all``` + +## Outputs +Two files are created for each Terraform Cloud workspace: + +- _workspace-name_-workspace.json +- _workspace-name_-variables.json + +Variable Sets are exported to files named `varset-_variable-set-name_.json` +with spaces in the variable set name replaced with hyphens (`-`). + +## Restrictions +The code assumes that all of the Terraform Cloud Variable Sets are contained +within the first result page of 20 entries. diff --git a/tfc-dump.pl b/tfc-dump.pl new file mode 100755 index 0000000..deca863 --- /dev/null +++ b/tfc-dump.pl @@ -0,0 +1,144 @@ +#!/usr/bin/perl +# +# tfc-dump.pl - dump Terraform Cloud workspace and variable information +# +# Usage: tfc-dump.pl --org=org-name {--workspace=name | --all} [--help] +# +# For the supplied Terraform Cloud workspace name, dump the workspace +# and variable information in JSON format. +# +# A Terraform Cloud access token must be supplied in the ATLAS_TOKEN environment +# variable. +# +# Uses curl(1), jq(1), tfc-ops. +# +# Dale Newby +# SIL International +# 12/2/2022 + +use strict; +use warnings; +use Getopt::Long qw(GetOptions); + +my $usage = "Usage: $0 --org=org-name {--workspace=name | --all} [--help]\n"; +my $tfc_org_name; # Terraform Cloud organization name +my $tfc_workspace_name; # Terraform Cloud workspace name +my $tfc_workspace_id; # Terraform Cloud workspace ID +my $all_workspaces; +my $help; + +Getopt::Long::Configure qw(gnu_getopt); +GetOptions( + 'org|o=s' => \$tfc_org_name, + 'workspace|w=s' => \$tfc_workspace_name, + 'all|a' => \$all_workspaces, + 'help|h' => \$help +) or die $usage; + +die $usage if (!defined($tfc_org_name) || defined($help)); +die $usage if ( defined($tfc_workspace_name) && defined($all_workspaces)); # can't have both +die $usage if (!defined($tfc_workspace_name) && !defined($all_workspaces)); # must have one + +if (! $ENV{ATLAS_TOKEN}) { + print STDERR "Terraform Cloud access token must be in ATLAS_TOKEN environment variable.\n"; + die $usage; +} + +my $curl_header1 = "--header \"Authorization: Bearer $ENV{ATLAS_TOKEN}\""; +my $curl_header2 = "--header \"Content-Type: application/vnd.api+json\""; +my $curl_headers = "$curl_header1 $curl_header2"; +my $curl_query; +my $curl_cmd; +my $jq_cmd; +my %workspace_list; +if (defined($tfc_workspace_name)) { # One workspace desired + + # Get the workspace ID given the workspace name. + + $curl_query = "\"https://app.terraform.io/api/v2/organizations/${tfc_org_name}/workspaces/${tfc_workspace_name}\""; + $curl_cmd = "curl $curl_headers $curl_query"; + $jq_cmd = "jq '.data.id'"; + + $tfc_workspace_id = `$curl_cmd | $jq_cmd`; + $tfc_workspace_id =~ s/"//g; + chomp($tfc_workspace_id); + + $workspace_list{$tfc_workspace_name} = $tfc_workspace_id; +} +else { # All workspaces desired + my $tfc_ops_cmd = "tfc-ops workspaces list --organization ${tfc_org_name} --attributes name,id"; + + my @result = `$tfc_ops_cmd`; + + # tfc-ops prints two header lines before the data we want to see. + shift(@result); # remove "Getting list of workspaces ..." + shift(@result); # remove "name, id" + chomp(@result); # remove newlines + + my $name; + my $id; + foreach (@result) { + ($name, $id) = split(/, /, $_); + $workspace_list{$name} = $id; + } +} + +# Dump the workspace and variable data to files. + +foreach (sort keys %workspace_list) { + + # Dump the workspace info + $curl_query = "\"https://app.terraform.io/api/v2/workspaces/$workspace_list{$_}\""; + $curl_cmd = "curl $curl_headers --output $_-workspace.json $curl_query"; + system($curl_cmd); + + # Dump the variables info + $curl_query = "\"https://app.terraform.io/api/v2/workspaces/$workspace_list{$_}/vars\""; + $curl_cmd = "curl $curl_headers --output $_-variables.json $curl_query"; + system($curl_cmd); +} + +# Dump the variable sets data to files. + +### WARNING ### +# +# This code assumes that all of the TFC Variable Sets are contained within +# the first result page of 20 entries. This was true for SIL in December 2022. +# +#### + +my @vs_names; +my @vs_ids; +my $tmpfile = `mktemp`; +chomp($tmpfile); + +$curl_query = "\"https://app.terraform.io/api/v2/organizations/${tfc_org_name}/varsets\""; +$curl_cmd = "curl $curl_headers --output $tmpfile $curl_query"; +system($curl_cmd); + +# Get the Variable Set names + +$jq_cmd = "cat $tmpfile | jq '.data[].attributes.name'"; +@vs_names = `$jq_cmd`; +# Remove the double quotes in each element of the array. +grep($_ =~ s/"//g && 0, @vs_names); # Programming Perl, p. 221, 1990 +chomp(@vs_names); + +# Get the Variable Set IDs + +$jq_cmd = "cat $tmpfile | jq '.data[].id'"; +@vs_ids = `$jq_cmd`; +# Remove the double quotes in each element of the array. +grep($_ =~ s/"//g && 0, @vs_ids); # Programming Perl, p. 221, 1990 +chomp(@vs_ids); + +my $filename; +for (my $ii = 0; $ii < scalar @vs_names; $ii++) { + $filename = $vs_names[$ii]; + $filename =~ s/ /-/g; # replace spaces with hyphens + $curl_query = "\"https://app.terraform.io/api/v2/varsets/$vs_ids[$ii]\""; + $curl_cmd = "curl $curl_headers --output varset-${filename}.json $curl_query"; + system($curl_cmd); +} + +exit(0); From d8e647400b6f7f2a70c94aebef33e1b59ecfda14 Mon Sep 17 00:00:00 2001 From: Dale Newby Date: Tue, 6 Dec 2022 11:19:53 -0500 Subject: [PATCH 2/4] updated formatting --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 498dc14..dbf7439 100644 --- a/README.md +++ b/README.md @@ -16,17 +16,17 @@ which covers most of what we need. 2. Obtain a Terraform Cloud access token. 3. `export ATLAS\_TOKEN=_terraform-cloud-access-token_` 4. To dump one workspace: -```tfc-dump.pl --org=_terraform-cloud-organization_ --workspace=_terraform-cloud-workspace-name_``` +`tfc-dump.pl --org=`_terraform-cloud-organization_` --workspace=`_terraform-cloud-workspace-name_ 5. To dump all workspaces in an organization: -```tfc-dump.pl --org=_terraform-cloud-organization_ --all``` +`tfc-dump.pl --org=`_terraform-cloud-organization_` --all` ## Outputs Two files are created for each Terraform Cloud workspace: -- _workspace-name_-workspace.json -- _workspace-name_-variables.json +- _workspace-name_`-workspace.json` +- _workspace-name_`-variables.json` -Variable Sets are exported to files named `varset-_variable-set-name_.json` +Variable Sets are exported to files named `varset-`_variable-set-name_`.json` with spaces in the variable set name replaced with hyphens (`-`). ## Restrictions From 01bbbccace83998d279655205a06a008ecd597aa Mon Sep 17 00:00:00 2001 From: Dale Newby Date: Tue, 6 Dec 2022 11:42:03 -0500 Subject: [PATCH 3/4] adjust formatting --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index dbf7439..be4bccf 100644 --- a/README.md +++ b/README.md @@ -14,19 +14,19 @@ which covers most of what we need. ## How to use tfc-dump.pl 1. Install [tfc-ops](https://github.com/silinternational/tfc-ops). 2. Obtain a Terraform Cloud access token. -3. `export ATLAS\_TOKEN=_terraform-cloud-access-token_` +3. export ATLAS\_TOKEN=_terraform-cloud-access-token_ 4. To dump one workspace: -`tfc-dump.pl --org=`_terraform-cloud-organization_` --workspace=`_terraform-cloud-workspace-name_ +tfc-dump.pl --org _terraform-cloud-organization_ --workspace _terraform-cloud-workspace-name_ 5. To dump all workspaces in an organization: -`tfc-dump.pl --org=`_terraform-cloud-organization_` --all` +tfc-dump.pl --org _terraform-cloud-organization_ --all ## Outputs Two files are created for each Terraform Cloud workspace: -- _workspace-name_`-workspace.json` -- _workspace-name_`-variables.json` +- _workspace-name_-workspace.json +- _workspace-name_-variables.json -Variable Sets are exported to files named `varset-`_variable-set-name_`.json` +Variable Sets are exported to files named varset-_variable-set-name_.json with spaces in the variable set name replaced with hyphens (`-`). ## Restrictions From 086cc9cedd3aad83385376c08cd679ff42dc5f9a Mon Sep 17 00:00:00 2001 From: Dale Newby Date: Fri, 9 Dec 2022 09:35:31 -0500 Subject: [PATCH 4/4] Updates from code review --- README.md | 5 +++-- tfc-dump.pl | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index be4bccf..54afe16 100644 --- a/README.md +++ b/README.md @@ -12,9 +12,10 @@ The exported information includes workspaces, variables, and variable sets which covers most of what we need. ## How to use tfc-dump.pl -1. Install [tfc-ops](https://github.com/silinternational/tfc-ops). +1. Install [tfc-ops](https://github.com/silinternational/tfc-ops). `tfc-dump` +was tested with `tfc-ops` version 3.0.0. 2. Obtain a Terraform Cloud access token. -3. export ATLAS\_TOKEN=_terraform-cloud-access-token_ +3. Set and export the environment variable ATLAS\_TOKEN with the Terraform Cloud access token as its value (e.g., use a command like export ATLAS\_TOKEN=_terraform-cloud-access-token_). Note that preceding the `export` command with a space may prevent the command from being stored in the shell history. Refer to the description of the `HISTCONTROL` shell variable in the `bash` man page for details. 4. To dump one workspace: tfc-dump.pl --org _terraform-cloud-organization_ --workspace _terraform-cloud-workspace-name_ 5. To dump all workspaces in an organization: diff --git a/tfc-dump.pl b/tfc-dump.pl index deca863..45dcd25 100755 --- a/tfc-dump.pl +++ b/tfc-dump.pl @@ -10,11 +10,12 @@ # A Terraform Cloud access token must be supplied in the ATLAS_TOKEN environment # variable. # -# Uses curl(1), jq(1), tfc-ops. +# Uses curl(1), jq(1), tfc-ops(https://github.com/silinternational/tfc-ops). +# Version 3.0.0 of tfc-ops was used during development. # # Dale Newby # SIL International -# 12/2/2022 +# December 2, 2022 use strict; use warnings;