Skip to content

Commit

Permalink
Merge pull request #1 from silinternational/develop
Browse files Browse the repository at this point in the history
Initial release of tfc-dump.pl
  • Loading branch information
dalenewby authored Dec 9, 2022
2 parents db9a716 + 086cc9c commit 9440e98
Show file tree
Hide file tree
Showing 3 changed files with 182 additions and 1 deletion.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,6 @@ inc/
/MANIFEST.bak
/pm_to_blib
/*.zip

# tfc-dump creates .json files that don't belong in version control
*.json
35 changes: 34 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,35 @@
# 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). `tfc-dump`
was tested with `tfc-ops` version 3.0.0.
2. Obtain a 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:
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.
145 changes: 145 additions & 0 deletions tfc-dump.pl
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
#!/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(https://github.com/silinternational/tfc-ops).
# Version 3.0.0 of tfc-ops was used during development.
#
# Dale Newby
# SIL International
# December 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);

0 comments on commit 9440e98

Please sign in to comment.