diff --git a/README.md b/README.md index 994e2ee..74b3df9 100644 --- a/README.md +++ b/README.md @@ -22,20 +22,50 @@ Terraform cloud scripts ### Usage: ```shell -terraform-cloud [arguments] +terraform-cloud [arguments] ``` OR ```shell -tfc [arguments] +tfc [arguments] ``` -### Available Allowed Resources: +### Available Commands and Subcommands: - `workspace` + - `get` - Prints the provided workspace as output + + Example: + ```shell + $ tfc workspace get orgnization/workspace + ``` + - `list` - List all workspaces in Organization. + + Note: In case of missing organization name, Throws an error and outputs all available organizations in the account. + + Example: + ```shell + $ tfc workspace list + + Error: Organization Missing + Please pass organization from list below: + - OrgA + - OrgB + + + Terraform Cloud - Devops Scripts + + Github: https://github.com/phenixcoder/devops-scripts + + Usage: + terraform-cloud [arguments] + OR + tfc [arguments] + + ``` + OR + ```shell + $ tfc workspace list orgnization + ``` - `output` + - `get` - Get the first output from latest state and prints as output + - `list` - Lista all output in the state. - `state` - -### Available Allowed Actions: - - `get` - - `set` - - `list` - ---- \ No newline at end of file + - `get` - Get the latest state and prints as output \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index d66a83b..f854f38 100644 --- a/package-lock.json +++ b/package-lock.json @@ -485,6 +485,11 @@ "integrity": "sha1-oMoMvCmltz6Dbuvhy/bF4OTrgvk=", "dev": true }, + "array-back": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", + "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==" + }, "array-ify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", @@ -733,6 +738,17 @@ "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", "dev": true }, + "command-line-args": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-5.2.0.tgz", + "integrity": "sha512-4zqtU1hYsSJzcJBOcNZIbW5Fbk9BkjCp1pZVhQKoRaWL5J7N4XphDLwo8aWwdQpTugxwu+jf9u2ZhkXiqp5Z6A==", + "requires": { + "array-back": "^3.1.0", + "find-replace": "^3.0.0", + "lodash.camelcase": "^4.3.0", + "typical": "^4.0.0" + } + }, "commitizen": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/commitizen/-/commitizen-4.2.4.tgz", @@ -1277,6 +1293,14 @@ "merge": "^2.1.0" } }, + "find-replace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-3.0.0.tgz", + "integrity": "sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==", + "requires": { + "array-back": "^3.0.1" + } + }, "find-root": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", @@ -1956,6 +1980,11 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, + "lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=" + }, "lodash.capitalize": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz", @@ -5221,6 +5250,11 @@ "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", "dev": true }, + "typical": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz", + "integrity": "sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==" + }, "uglify-js": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.14.1.tgz", diff --git a/package.json b/package.json index 7282b6b..ddd65a6 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ } }, "dependencies": { + "command-line-args": "^5.2.0", "follow-redirects": "^1.14.1" } } diff --git a/scripts/shared/environment.js b/scripts/shared/environment.js index 14656ce..c622dfb 100644 --- a/scripts/shared/environment.js +++ b/scripts/shared/environment.js @@ -1,24 +1,48 @@ +const { existsSync, readFileSync } = require('fs'); +const path = require('path'); + +const currentEnv = { + TFCRC_PATH: undefined, + config: undefined +} const Environment = (render) => { - const env = { - // args: process.argv, - paths: { - pwd: process.env.PWD, - home: process.env.HOME, - }, - secrets: { - TFC_TOKEN: process.env.TFC_TOKEN, - TFC_WORKSPACE: process.env.TFC_WORKSPACE, - }, - action: process.argv[3] || '', - resource: process.argv[2] || '', - args: process.argv.slice(4) + if (!currentEnv.config) { + const TFCRC_PATH = path.join(process.env.PWD, '.tfcrc'); + const TFC_CONFIG = {}; + if (existsSync(TFCRC_PATH)) { + currentEnv.TFCRC_PATH = TFCRC_PATH; + + readFileSync(TFCRC_PATH, { encoding: 'utf-8' }) + .split('\n').forEach(line => { + const [key, value] = line.split('='); + if(key && value) { + TFC_CONFIG[key] = value; + } + }); + } + + currentEnv.config = { + // args: process.argv, + paths: { + pwd: process.env.PWD, + home: process.env.HOME, + }, + secrets: { + TFC_TOKEN: process.env.TFC_TOKEN || TFC_CONFIG.TFC_TOKEN, + TFC_WORKSPACE: process.env.TFC_WORKSPACE || TFC_CONFIG.TFC_WORKSPACE || TFC_CONFIG.WORKSPACE_ID, + }, + action: process.argv[3] || '', + resource: process.argv[2] || '', + args: process.argv.slice(4) + } } + if (render) { console.log('Envrionment'); - console.log(env); + console.log(currentEnv.config); } - return env; + return currentEnv.config; } module.exports = Environment; \ No newline at end of file diff --git a/scripts/terraform/cli.js b/scripts/terraform/cli.js index 0b71261..54bfe57 100755 --- a/scripts/terraform/cli.js +++ b/scripts/terraform/cli.js @@ -60,6 +60,7 @@ if ( console.log(JSON.stringify(value, null, ' ')); } }).catch(err => { + // console.log(err); Help(err); }); } diff --git a/scripts/terraform/help.js b/scripts/terraform/help.js index 3e3721b..9077189 100644 --- a/scripts/terraform/help.js +++ b/scripts/terraform/help.js @@ -1,6 +1,7 @@ function Help(errorMessage) { if (errorMessage) { console.error(`Error: ${errorMessage}`); + // console.trace() } console.log(` diff --git a/scripts/terraform/resources/output.js b/scripts/terraform/resources/output.js index 6e89885..1901040 100755 --- a/scripts/terraform/resources/output.js +++ b/scripts/terraform/resources/output.js @@ -3,29 +3,44 @@ const Request = require("../../shared/request"); const Help = require("../help"); const OUTPUT = { + list: async (...args) => { + const env = Environment(); + if (!env.secrets.TFC_TOKEN) { + Help('Missing TFC_TOKEN.'); + } + if (!env.secrets.TFC_WORKSPACE) { + Help('Missing TFC_WORKSPACE.'); + } + const state = await Request('app.terraform.io', 'GET', `/api/v2/workspaces/${env.secrets.TFC_WORKSPACE}/current-state-version`, { + 'Authorization': `Bearer ${env.secrets.TFC_TOKEN}` + }); + const stateJSON = JSON.parse(state); + stateJSON.data.relationships.outputs.data.forEach((output, i) => { + console.log(i, output.id); + }); + }, get: async (...args) => { const env = Environment(); - if (env.secrets.TFC_TOKEN) { + if (!env.secrets.TFC_TOKEN) { Help('Missing TFC_TOKEN.'); } - if (env.secrets.TFC_WORKSPACE) { + if (!env.secrets.TFC_WORKSPACE) { Help('Missing TFC_WORKSPACE.'); } const state = await Request('app.terraform.io', 'GET', `/api/v2/workspaces/${env.secrets.TFC_WORKSPACE}/current-state-version`, { 'Authorization': `Bearer ${env.secrets.TFC_TOKEN}` }); - console.log(state); - const output_id = JSON.parse(state).data.relationships.outputs.data[0].id; - + let outputs = await Request('app.terraform.io', 'GET', `/api/v2/state-version-outputs/${output_id}`, { 'Authorization': `Bearer ${env.secrets.TFC_TOKEN}` }); outputs = JSON.parse(outputs).data.attributes.value; - console.log(outputs.byString(env.args[0] || '')); + // console.log(outputs.byString(env.args[0] || '')); + console.log(JSON.stringify(outputs, null, ' ')); }, } diff --git a/scripts/terraform/resources/state.js b/scripts/terraform/resources/state.js index 70877a2..43a3cd1 100755 --- a/scripts/terraform/resources/state.js +++ b/scripts/terraform/resources/state.js @@ -1,6 +1,28 @@ +const Environment = require("../../shared/environment"); +const Request = require("../../shared/request"); +const Help = require("../help"); +const URL = require('url'); +const { writeFileSync, existsSync } = require("fs"); + const OUTPUT = { - get: () => { - throw "not implimented"; + get: async (...args) => { + const env = Environment(); + if (!env.secrets.TFC_TOKEN) { + Help('Missing TFC_TOKEN.'); + } + if (!env.secrets.TFC_WORKSPACE) { + Help('Missing TFC_WORKSPACE.'); + } + const response = await Request('app.terraform.io', 'GET', `/api/v2/workspaces/${env.secrets.TFC_WORKSPACE}/current-state-version`, { + 'Authorization': `Bearer ${env.secrets.TFC_TOKEN}` + }); + + const stateURL = URL.parse(JSON.parse(response).data.attributes['hosted-state-download-url']); + const stateResponse = await Request(stateURL.hostname, 'GET', stateURL.path, { + 'Authorization': `Bearer ${env.secrets.TFC_TOKEN}` + }); + + console.log(stateResponse) }, set: () => { diff --git a/scripts/terraform/resources/workspace.js b/scripts/terraform/resources/workspace.js index 007a4be..d0508bf 100755 --- a/scripts/terraform/resources/workspace.js +++ b/scripts/terraform/resources/workspace.js @@ -19,8 +19,16 @@ const WORKSPACE = { list: async (args, returnOnly) => { if (!args[0]) { - throw 'Organisation Missing' + let responseOrgs = await Request('app.terraform.io', 'GET', `/api/v2/organizations/`, { + 'Authorization': `Bearer ${Environment().secrets.TFC_TOKEN}` + }); + let errorString = 'Organization Missing\nPlease pass organization from list below:\n'; + JSON.parse(responseOrgs).data.forEach(org => { + errorString += ` - ${org.id}\n` + }) + throw errorString; } + const org = args[0]; let response = await Request('app.terraform.io', 'GET', `/api/v2/organizations/${org}/workspaces`, { @@ -29,6 +37,9 @@ const WORKSPACE = { response = JSON.parse(response); if (response.errors) { response.errors.forEach(error => { + if (error.status == 404) { + throw `Organization ${org} not found.` + } console.log(`error: [${error.status}] ${error.title}`); }); throw "";