@agilecustoms/envctl 0.26.0 → 0.27.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -7,8 +7,8 @@
7
7
 
8
8
  ```shell
9
9
  terraform init -backend-config=key=laxa1986
10
- # TODO: remove --env in next minor release
11
- envctl deploy --env laxa1986 -var-file=versions.tfvars -var="log_level=debug"
10
+ envctl deploy -var-file=versions.tfvars -var="log_level=debug"
11
+ envctl delete
12
12
  ```
13
13
 
14
14
  ## setup/update
@@ -0,0 +1,8 @@
1
+ export class S3Backend {
2
+ getType() {
3
+ return 's3';
4
+ }
5
+ getKey(config) {
6
+ return config['key'];
7
+ }
8
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -1,12 +1,7 @@
1
1
  import path from 'path';
2
2
  import inquirer from 'inquirer';
3
- import { KnownException } from '../exceptions.js';
4
- import { ConfigService } from '../service/index.js';
5
3
  export class CliHelper {
6
- configService;
7
- constructor(configService) {
8
- this.configService = configService;
9
- }
4
+ constructor() { }
10
5
  async promptYesNo(message, defaultValue = false) {
11
6
  const { answer } = await inquirer.prompt([{
12
7
  type: 'confirm',
@@ -16,34 +11,8 @@ export class CliHelper {
16
11
  }]);
17
12
  return answer;
18
13
  }
19
- ensureEnv(key) {
20
- if (key) {
21
- return key;
22
- }
23
- const owner = this.configService.getOwner();
24
- if (!owner) {
25
- throw new KnownException('--key argument is not provided\n'
26
- + 'default to owner from local configuration, but it is not configured\n'
27
- + 'please call with --key argument or run \'envctl configure\' to configure owner');
28
- }
29
- console.log(`Key is not provided, default to owner name ${owner}`);
30
- return owner;
31
- }
32
- ensureOwner(owner) {
33
- if (!owner) {
34
- owner = this.configService.getOwner();
35
- if (!owner) {
36
- throw new KnownException('when called without --owner option, first call \'envctl configure\'');
37
- }
38
- console.log(`Owner not provided, default to configured owner ${owner}`);
39
- }
40
- return owner;
41
- }
42
- ensureKind(kind, cwd) {
43
- if (kind) {
44
- return kind;
45
- }
46
- kind = getDirName(cwd);
14
+ getKind(cwd) {
15
+ const kind = getDirName(cwd);
47
16
  console.log(`Inferred kind from directory name: ${kind}`);
48
17
  return kind;
49
18
  }
@@ -1,14 +1,7 @@
1
1
  import { spawn } from 'child_process';
2
- import path from 'path';
3
2
  import * as readline from 'readline';
4
- import { fileURLToPath } from 'url';
5
3
  import { ProcessException } from '../exceptions.js';
6
4
  export class ProcessRunner {
7
- async runScript(script, args = [], cwd, scanner) {
8
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
9
- const scriptPath = path.join(__dirname, `../../scripts/${script}`);
10
- await this.run(scriptPath, args, cwd, scanner);
11
- }
12
5
  async run(command, args, cwd, out_scanner, in_scanner) {
13
6
  const spawnOptions = {
14
7
  stdio: ['pipe', 'pipe', 'pipe'],
@@ -10,9 +10,48 @@ const RETRYABLE_ERRORS = [
10
10
  export class TerraformAdapter {
11
11
  processRunner;
12
12
  cliHelper;
13
- constructor(processRunner, cliHelper) {
13
+ backends;
14
+ constructor(processRunner, cliHelper, backends) {
14
15
  this.processRunner = processRunner;
15
16
  this.cliHelper = cliHelper;
17
+ this.backends = backends.reduce((acc, backend) => {
18
+ acc.set(backend.getType(), backend);
19
+ return acc;
20
+ }, new Map());
21
+ }
22
+ getKey(cwd) {
23
+ const dir = cwd ?? process.cwd();
24
+ const statePath = path.join(dir, '.terraform', 'terraform.tfstate');
25
+ if (!fs.existsSync(statePath)) {
26
+ throw new KnownException(`Terraform state file not found at: ${statePath}`);
27
+ }
28
+ let content;
29
+ try {
30
+ content = fs.readFileSync(statePath, 'utf8');
31
+ }
32
+ catch (err) {
33
+ throw new KnownException(`Failed to read terraform state file: ${statePath}`, { cause: err });
34
+ }
35
+ let tfstate;
36
+ try {
37
+ tfstate = JSON.parse(content);
38
+ }
39
+ catch (err) {
40
+ throw new KnownException(`Failed to parse terraform state file: ${statePath}`, { cause: err });
41
+ }
42
+ const backendJson = tfstate.backend;
43
+ const type = backendJson?.type;
44
+ const backend = this.backends.get(type);
45
+ if (backend === undefined) {
46
+ const supportedBackends = Array.from(this.backends.keys()).join(',');
47
+ throw new KnownException(`Unsupported terraform backend type: ${type}. Supported types: ${supportedBackends}`);
48
+ }
49
+ const config = backendJson?.config;
50
+ const key = backend.getKey(config);
51
+ if (!key) {
52
+ throw new KnownException(`Terraform backend config does not contain 'key' attribute`);
53
+ }
54
+ return key;
16
55
  }
17
56
  printTime() {
18
57
  const now = new Date();
@@ -6,12 +6,11 @@ export function createEphemeral(program) {
6
6
  program
7
7
  .command('create-ephemeral')
8
8
  .description('Create bare env w/o resources. Used in CI as first step just to pre-generate token for extension')
9
- .requiredOption('--key <key>', _keys.KEY)
10
- .option('--owner <owner>', _keys.OWNER)
9
+ .option('--cwd <cwd>', _keys.CWD)
11
10
  .action(wrap(handler));
12
11
  }
13
12
  async function handler(options) {
14
- const { key, owner } = options;
15
- const token = await envCtl.createEphemeral(key, owner);
13
+ const { cwd } = options;
14
+ const token = await envCtl.createEphemeral(cwd);
16
15
  console.log(token);
17
16
  }
@@ -1,5 +1,5 @@
1
1
  import { Command } from 'commander';
2
- import { cliHelper, envCtl } from '../container.js';
2
+ import { envCtl } from '../container.js';
3
3
  import { _keys } from './_keys.js';
4
4
  import { wrap } from './_utils.js';
5
5
  export function deleteIt(program) {
@@ -13,6 +13,5 @@ export function deleteIt(program) {
13
13
  }
14
14
  async function handler(options) {
15
15
  const { key, force, cwd } = options;
16
- const theKey = cliHelper.ensureEnv(key);
17
- await envCtl.delete(theKey, Boolean(force), cwd);
16
+ await envCtl.delete(Boolean(force), key, cwd);
18
17
  }
@@ -6,9 +6,6 @@ export function deploy(program) {
6
6
  program
7
7
  .command('deploy')
8
8
  .description('Create new or update existing dev environment')
9
- .option('--key <key>', _keys.KEY)
10
- .option('--owner <owner>', _keys.OWNER)
11
- .option('--kind <kind>', _keys.KIND)
12
9
  .option('--cwd <cwd>', _keys.CWD)
13
10
  .allowUnknownOption(true)
14
11
  .argument('[args...]')
@@ -16,6 +13,6 @@ export function deploy(program) {
16
13
  }
17
14
  async function handler(tfArgs, options) {
18
15
  await awsCredsHelper.ensureCredentials();
19
- const { cwd, ...envDto } = options;
20
- await envCtl.deploy(envDto, tfArgs, cwd);
16
+ const { cwd } = options;
17
+ await envCtl.deploy(tfArgs, cwd);
21
18
  }
@@ -1,5 +1,5 @@
1
1
  import { Command } from 'commander';
2
- import { cliHelper, envCtl } from '../container.js';
2
+ import { envCtl } from '../container.js';
3
3
  import { _keys } from './_keys.js';
4
4
  import { wrap } from './_utils.js';
5
5
  export function destroy(program) {
@@ -7,8 +7,8 @@ export function destroy(program) {
7
7
  .command('destroy')
8
8
  .description('Destroy environment resources. This is thin wrapper for terraform destroy.'
9
9
  + ' Unlike "delete" command (which just schedule deletion) this command deletes resources synchronously.'
10
+ + ' Unlike "delete" command, this command doesn\'t have --key option, it can only delete env after key you provided during terraform init.'
10
11
  + ' Main use case - test deletion process, basically that you have enough permissions to delete resources')
11
- .option('--key <key>', _keys.KEY)
12
12
  .option('--force', _keys.FORCE)
13
13
  .option('--cwd <cwd>', _keys.CWD)
14
14
  .allowUnknownOption(true)
@@ -16,7 +16,6 @@ export function destroy(program) {
16
16
  .action(wrap(handler));
17
17
  }
18
18
  async function handler(tfArgs, options) {
19
- const { key, force, cwd } = options;
20
- const theKey = cliHelper.ensureEnv(key);
21
- await envCtl.destroy(theKey, tfArgs, Boolean(force), cwd);
19
+ const { force, cwd } = options;
20
+ await envCtl.destroy(tfArgs, Boolean(force), cwd);
22
21
  }
@@ -7,8 +7,6 @@ export function plan(program) {
7
7
  .command('plan')
8
8
  .description('High level wrapper for terraform plan. Compliments deploy command: if you plan to deploy env with envctl deploy,'
9
9
  + ' then it is recommended to do plan with envctl plan to guarantee consistent behavior')
10
- .option('--key <key>', _keys.KEY)
11
- .option('--owner <owner>', _keys.OWNER)
12
10
  .option('--cwd <cwd>', _keys.CWD)
13
11
  .allowUnknownOption(true)
14
12
  .argument('[args...]')
@@ -16,6 +14,6 @@ export function plan(program) {
16
14
  }
17
15
  async function handler(tfArgs, options) {
18
16
  await awsCredsHelper.ensureCredentials();
19
- const { cwd, ...envDto } = options;
20
- await envCtl.plan(envDto, tfArgs, cwd);
17
+ const { cwd } = options;
18
+ await envCtl.plan(tfArgs, cwd);
21
19
  }
@@ -1,5 +1,5 @@
1
1
  import { Command } from 'commander';
2
- import { cliHelper, envCtl } from '../container.js';
2
+ import { envCtl } from '../container.js';
3
3
  import { _keys } from './_keys.js';
4
4
  import { wrap } from './_utils.js';
5
5
  export function status(program) {
@@ -12,6 +12,5 @@ export function status(program) {
12
12
  }
13
13
  async function handler(options) {
14
14
  const { key, cwd } = options;
15
- const theKey = cliHelper.ensureEnv(key);
16
- await envCtl.status(theKey, cwd);
15
+ await envCtl.status(key, cwd);
17
16
  }
package/dist/container.js CHANGED
@@ -1,13 +1,17 @@
1
+ import { S3Backend } from './backend/S3Backend.js';
1
2
  import { AwsCredsHelper } from './client/AwsCredsHelper.js';
2
3
  import { CliHelper, EnvApiClient, HttpClient, TerraformAdapter } from './client/index.js';
3
4
  import { ProcessRunner } from './client/ProcessRunner.js';
4
5
  import { ConfigService, EnvCtl } from './service/index.js';
6
+ const cliHelper = new CliHelper();
5
7
  const configService = new ConfigService();
6
- const cliHelper = new CliHelper(configService);
7
8
  const awsCredsHelper = new AwsCredsHelper();
9
+ const backends = [
10
+ new S3Backend()
11
+ ];
8
12
  const httpClient = new HttpClient(awsCredsHelper);
9
13
  const envApiClient = new EnvApiClient(httpClient);
10
14
  const processRunner = new ProcessRunner();
11
- const terraformAdapter = new TerraformAdapter(processRunner, cliHelper);
12
- const envCtl = new EnvCtl(cliHelper, envApiClient, terraformAdapter);
13
- export { awsCredsHelper, cliHelper, configService, envCtl };
15
+ const terraformAdapter = new TerraformAdapter(processRunner, cliHelper, backends);
16
+ const envCtl = new EnvCtl(cliHelper, envApiClient, terraformAdapter, configService);
17
+ export { awsCredsHelper, configService, envCtl };
@@ -1,6 +1,7 @@
1
1
  import * as fs from 'node:fs';
2
2
  import * as os from 'node:os';
3
3
  import path from 'path';
4
+ import { KnownException } from '../exceptions.js';
4
5
  const CONFIG_PATH = path.join(os.homedir(), '.envctl', 'config.json');
5
6
  export class ConfigService {
6
7
  config;
@@ -23,6 +24,10 @@ export class ConfigService {
23
24
  return this.config;
24
25
  }
25
26
  getOwner() {
26
- return this.loadConfig()?.owner;
27
+ const owner = this.loadConfig()?.owner;
28
+ if (!owner) {
29
+ throw new KnownException('please first set owner via calling \'envctl configure\'');
30
+ }
31
+ return owner;
27
32
  }
28
33
  }
@@ -4,32 +4,37 @@ export class EnvCtl {
4
4
  cliHelper;
5
5
  envApi;
6
6
  terraformAdapter;
7
- constructor(cliHelper, envApi, terraformAdapter) {
7
+ configService;
8
+ constructor(cliHelper, envApi, terraformAdapter, configService) {
8
9
  this.cliHelper = cliHelper;
9
10
  this.envApi = envApi;
10
11
  this.terraformAdapter = terraformAdapter;
12
+ this.configService = configService;
13
+ }
14
+ getKey(key, cwd) {
15
+ if (!key) {
16
+ console.log('Key is not provided, inferring from .terraform/terraform.tfstate file');
17
+ key = this.terraformAdapter.getKey(cwd);
18
+ }
19
+ return key;
11
20
  }
12
21
  async status(key, cwd) {
22
+ key = this.getKey(key, cwd);
13
23
  console.log(`Retrieve env ${key}`);
14
24
  const env = await this.envApi.get(key);
15
25
  if (env === null) {
16
26
  console.log(`Env ${key} does not exist`);
17
27
  return;
18
28
  }
19
- const kind = env.kind ? this.cliHelper.ensureKind(undefined, cwd) : undefined;
20
29
  console.log(`Env ${key} status: ${env.status}`);
21
- if (env.kind && env.kind !== kind) {
22
- console.warn(`Env ${key} kind (dir-name): ${env.kind} - looks like this env was deployed from a different directory`);
30
+ if (env.kind) {
31
+ const kind = this.cliHelper.getKind(cwd);
32
+ if (env.kind !== kind) {
33
+ console.warn(`Env ${key} kind (dir-name): ${env.kind} - looks like this env was deployed from a different directory`);
34
+ }
23
35
  }
24
36
  }
25
- async tryGetEnv(input) {
26
- let key = input.key;
27
- let owner = input.owner;
28
- if (!key) {
29
- owner = this.cliHelper.ensureOwner(owner);
30
- console.log(`Env name not provided, default to owner name ${owner}`);
31
- key = owner;
32
- }
37
+ async tryGetEnv(key) {
33
38
  console.log(`Check if env ${key} already exists`);
34
39
  const env = await this.envApi.get(key);
35
40
  if (env) {
@@ -38,15 +43,19 @@ export class EnvCtl {
38
43
  else {
39
44
  console.log(`Env ${key} does not exist`);
40
45
  }
41
- return { anOwner: owner, key, env };
46
+ return env;
42
47
  }
43
- checkInput(env, input, cwd) {
44
- if (input.owner && env.owner !== input.owner) {
45
- throw new KnownException(`Can not change env owner ${env.owner} -> ${input.owner}`);
48
+ checkInput(env, owner, cwd) {
49
+ if (env.ephemeral)
50
+ return;
51
+ if (env.owner !== owner) {
52
+ throw new KnownException(`Can not change env owner ${env.owner} -> ${owner}`);
46
53
  }
47
- const kind = this.cliHelper.ensureKind(input.kind, cwd);
48
- if ((input.kind || env.kind) && kind !== env.kind) {
49
- throw new KnownException(`Can not change env kind ${env.kind} -> ${kind}`);
54
+ if (env.kind) {
55
+ const kind = this.cliHelper.getKind(cwd);
56
+ if (kind !== env.kind) {
57
+ throw new KnownException(`Can not change env kind ${env.kind} -> ${kind}`);
58
+ }
50
59
  }
51
60
  }
52
61
  checkStatus(env, availableStatuses = [EnvStatus.Init, EnvStatus.Deploying, EnvStatus.Active, EnvStatus.Updating]) {
@@ -58,15 +67,16 @@ export class EnvCtl {
58
67
  }
59
68
  throw new KnownException(`Env ${env.key} status is ${env.status}, can not run this command`);
60
69
  }
61
- async plan(input, tfArgs, cwd) {
62
- const { anOwner, key, env } = await this.tryGetEnv(input);
70
+ async plan(tfArgs, cwd) {
71
+ const key = this.terraformAdapter.getKey(cwd);
72
+ const owner = this.configService.getOwner();
73
+ const env = await this.tryGetEnv(key);
63
74
  if (env == null) {
64
- const owner = this.cliHelper.ensureOwner(anOwner);
65
- await this.runPlan(key, owner, tfArgs, undefined, cwd);
75
+ const vars = undefined;
76
+ await this.runPlan(key, owner, tfArgs, vars, cwd);
66
77
  return;
67
78
  }
68
- this.checkInput(env, input, cwd);
69
- const owner = env.owner;
79
+ this.checkInput(env, owner, cwd);
70
80
  this.checkStatus(env);
71
81
  await this.promptUnlock(env);
72
82
  if (env.status !== EnvStatus.Active) {
@@ -78,25 +88,27 @@ export class EnvCtl {
78
88
  const envTerraform = { key, ephemeral: false, owner, args, vars };
79
89
  await this.terraformAdapter.plan(envTerraform, cwd);
80
90
  }
81
- async createEphemeral(key, owner) {
91
+ async createEphemeral(cwd) {
92
+ const key = this.terraformAdapter.getKey(cwd);
82
93
  const env = await this.envApi.get(key);
83
94
  if (env !== null) {
84
95
  throw new KnownException(`Env ${key} already exists`);
85
96
  }
86
- const createEnv = { key, owner };
97
+ const createEnv = { key };
87
98
  return await this.envApi.createEphemeral(createEnv);
88
99
  }
89
- async deploy(input, tfArgs, cwd) {
90
- const { anOwner, key, env } = await this.tryGetEnv(input);
100
+ async deploy(tfArgs, cwd) {
101
+ const key = this.terraformAdapter.getKey(cwd);
102
+ const owner = this.configService.getOwner();
103
+ const env = await this.tryGetEnv(key);
91
104
  if (env === null) {
92
- const owner = this.cliHelper.ensureOwner(anOwner);
93
- const kind = this.cliHelper.ensureKind(input.kind, cwd);
94
- console.log('Creating env tracking record in DynamoDB');
105
+ const kind = this.cliHelper.getKind(cwd);
106
+ console.log('Creating env metadata');
95
107
  const createEnv = { key, owner, kind };
96
108
  const newEnv = await this.envApi.create(createEnv);
97
109
  return await this.runDeploy(newEnv, key, tfArgs, cwd);
98
110
  }
99
- this.checkInput(env, input, cwd);
111
+ this.checkInput(env, owner, cwd);
100
112
  this.checkStatus(env);
101
113
  if (env.status === EnvStatus.Init) {
102
114
  await this.envApi.lockForUpdate(env);
@@ -134,7 +146,8 @@ export class EnvCtl {
134
146
  console.log('Activating env (to finish creation)');
135
147
  await this.envApi.activate(env);
136
148
  }
137
- async delete(key, force, cwd) {
149
+ async delete(force, key, cwd) {
150
+ key = this.getKey(key, cwd);
138
151
  const env = await this.get(key);
139
152
  if (!force) {
140
153
  this.checkStatus(env);
@@ -143,7 +156,7 @@ export class EnvCtl {
143
156
  console.log(`Env ${env.key} is deleted`);
144
157
  return;
145
158
  }
146
- const kind = this.cliHelper.ensureKind(undefined, cwd);
159
+ const kind = this.cliHelper.getKind(cwd);
147
160
  if (env.kind && env.kind !== kind) {
148
161
  const answerYes = await this.cliHelper.promptYesNo(`Env ${env.key} kind (dir-name): ${env.kind}\n`
149
162
  + 'You\'re deleting env deployed from different dir\n'
@@ -162,7 +175,8 @@ export class EnvCtl {
162
175
  const message = await this.envApi.delete(env);
163
176
  console.log(message);
164
177
  }
165
- async destroy(key, tfArgs, force, cwd) {
178
+ async destroy(tfArgs, force, cwd) {
179
+ const key = this.terraformAdapter.getKey(cwd);
166
180
  const env = await this.get(key);
167
181
  this.checkStatus(env);
168
182
  if (env.status === EnvStatus.Init) {
@@ -170,7 +184,7 @@ export class EnvCtl {
170
184
  return;
171
185
  }
172
186
  if (!force) {
173
- const kind = this.cliHelper.ensureKind(undefined, cwd);
187
+ const kind = this.cliHelper.getKind(cwd);
174
188
  if (env.kind && env.kind !== kind) {
175
189
  throw new KnownException(`Env ${env.key} kind (dir-name): ${env.kind} - make sure you run destroy from the same directory`);
176
190
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@agilecustoms/envctl",
3
3
  "description": "node.js CLI client for manage environments",
4
- "version": "0.26.0",
4
+ "version": "0.27.1",
5
5
  "author": "Alex Chekulaev",
6
6
  "type": "module",
7
7
  "bin": {
@@ -29,19 +29,19 @@
29
29
  "run-version": " tsc --sourceMap true && npm run run -- --version",
30
30
  "run-configure": "tsc --sourceMap true && npm run run -- configure",
31
31
  "~": "",
32
- "run-init": "cd ../tt-core && terraform init -backend-config=key=laxa1986 -upgrade",
32
+ "run-init": "cd ../tt-core && terraform init -upgrade -backend-config=key=laxa1986",
33
33
  "run-status": " tsc --sourceMap true && npm run run -- status --cwd ../tt-core",
34
34
  "run-plan": " tsc --sourceMap true && npm run run -- plan --cwd ../tt-core",
35
35
  "run-deploy": " tsc --sourceMap true && npm run run -- deploy --cwd ../tt-core -var=\"env_size=min\"",
36
36
  "run-delete": " tsc --sourceMap true && npm run run -- delete --cwd ../tt-core",
37
37
  "run-destroy": "tsc --sourceMap true && npm run run -- destroy --cwd ../tt-core",
38
38
  "-": "",
39
- "run-ephemeral-init": "cd ../tt-gitops && terraform init -backend-config=key=test",
40
- "run-ephemeral-create": " tsc --sourceMap true && npm run run -- create-ephemeral --env test",
41
- "run-ephemeral-status": " tsc --sourceMap true && npm run run -- status --env test",
42
- "run-ephemeral-deploy": " tsc --sourceMap true && npm run run -- deploy --env test --cwd ../tt-gitops -var-file=versions.tfvars",
43
- "run-ephemeral-delete": " tsc --sourceMap true && npm run run -- delete --env test --cwd ../tt-gitops --force",
44
- "run-ephemeral-destroy": "tsc --sourceMap true && npm run run -- destroy --env test --cwd ../tt-gitops --force -var-file=versions.tfvars"
39
+ "run-ephemeral-init": "cd ../tt-gitops && terraform init -upgrade -backend-config=key=test -reconfigure",
40
+ "run-ephemeral-create": " tsc --sourceMap true && npm run run -- create-ephemeral --cwd ../tt-gitops",
41
+ "run-ephemeral-status": " tsc --sourceMap true && npm run run -- status --key test",
42
+ "run-ephemeral-deploy": " tsc --sourceMap true && npm run run -- deploy --cwd ../tt-gitops -var-file=versions.tfvars",
43
+ "run-ephemeral-delete": " tsc --sourceMap true && npm run run -- delete --cwd ../tt-gitops --force",
44
+ "run-ephemeral-destroy": "tsc --sourceMap true && npm run run -- destroy --cwd ../tt-gitops --force -var-file=versions.tfvars"
45
45
  },
46
46
  "dependencies": {
47
47
  "@aws-sdk/client-sts": "^3.716.0",
@@ -1,8 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
-
4
- # TF state is stored in S3 in format: {company}-{acc-alias}-tf-state/{key}
5
- # (for non ephemeral environments the {key} typically has form of {project-code}-{env-name} like tt-dev)
6
- # Retrieve AWS account information
7
- acc_id=$(aws sts get-caller-identity --query "Account" --output text)
8
- aws organizations list-tags-for-resource --resource-id "$acc_id" --query "Tags[?Key=='Alias'].Value" --output text