From aa89505650e9f7873067d79f9abd8ea54a2365e3 Mon Sep 17 00:00:00 2001 From: Rob Gil Date: Thu, 16 Jan 2020 12:51:20 -0500 Subject: [PATCH] 169163334 - Abstracts terraform wrapper code The terraform wrapper is now abstracted in to a utility class for working with terraform. The terraform module was also updated to support configurable keyvault servers. Logging for this new module was also added, so the terraform output is seen on the console. --- terraform/secrets-tool/commands/terraform.py | 62 +++++++------------ terraform/secrets-tool/logging.yaml | 4 ++ .../secrets-tool/utils/terraform/__init__.py | 0 .../secrets-tool/utils/terraform/wrapper.py | 61 ++++++++++++++++++ 4 files changed, 88 insertions(+), 39 deletions(-) create mode 100644 terraform/secrets-tool/utils/terraform/__init__.py create mode 100644 terraform/secrets-tool/utils/terraform/wrapper.py diff --git a/terraform/secrets-tool/commands/terraform.py b/terraform/secrets-tool/commands/terraform.py index f34db59d..8fcdf55a 100644 --- a/terraform/secrets-tool/commands/terraform.py +++ b/terraform/secrets-tool/commands/terraform.py @@ -4,64 +4,48 @@ import logging import subprocess from utils.keyvault.secrets import SecretsClient +from utils.terraform.wrapper import TFWrapper logger = logging.getLogger(__name__) PROCESS='terraform' @click.group() +@click.option('--keyvault', required=True, help="Specify the keyvault to operate on") @click.pass_context -def terraform(ctx): - pass +def terraform(ctx, keyvault): + ctx.ensure_object(dict) + ctx.obj['keyvault'] = keyvault @click.command('plan') @click.pass_context def plan(ctx): - keyvault = SecretsClient(vault_url="https://cloudzero-dev-keyvault.vault.azure.net/") - # Set env variables for TF - for secret in keyvault.list_secrets(): - name = 'TF_VAR_' + secret - val = keyvault.get_secret(secret) - #print(val) - os.environ[name] = val - env = os.environ.copy() - command = "{} {}".format(PROCESS, 'plan') - with subprocess.Popen(command, env=env, stdout=subprocess.PIPE, shell=True) as proc: - for line in proc.stdout: - logging.info(line.decode("utf-8") ) + keyvault = SecretsClient(vault_url=ctx.obj['keyvault']) + tf = TFWrapper(keyvault) + tf.plan() @click.command('apply') @click.pass_context def apply(ctx): - keyvault = SecretsClient(vault_url="https://cloudzero-dev-keyvault.vault.azure.net/") - # Set env variables for TF - for secret in keyvault.list_secrets(): - name = 'TF_VAR_' + secret - val = keyvault.get_secret(secret) - #print(val) - os.environ[name] = val - env = os.environ.copy() - command = "{} {}".format(PROCESS, 'apply -auto-approve') - with subprocess.Popen(command, env=env, stdout=subprocess.PIPE, shell=True) as proc: - for line in proc.stdout: - logging.info(line.decode("utf-8") ) + keyvault = SecretsClient(vault_url=ctx.obj['keyvault']) + tf = TFWrapper(keyvault) + tf.apply() @click.command('destroy') @click.pass_context def destroy(ctx): - keyvault = SecretsClient(vault_url="https://cloudzero-dev-keyvault.vault.azure.net/") - # Set env variables for TF - for secret in keyvault.list_secrets(): - name = 'TF_VAR_' + secret - val = keyvault.get_secret(secret) - #print(val) - os.environ[name] = val - env = os.environ.copy() - command = "{} {}".format(PROCESS, 'destroy') - with subprocess.Popen(command, env=env, stdout=subprocess.PIPE, shell=True) as proc: - for line in proc.stdout: - logging.info(line.decode("utf-8") ) + keyvault = SecretsClient(vault_url=ctx.obj['keyvault']) + tf = TFWrapper(keyvault) + tf.destroy() + +@click.command('init') +@click.pass_context +def init(ctx): + keyvault = SecretsClient(vault_url=ctx.obj['keyvault']) + tf = TFWrapper(keyvault) + tf.init() terraform.add_command(plan) terraform.add_command(apply) -terraform.add_command(destroy) \ No newline at end of file +terraform.add_command(destroy) +terraform.add_command(init) \ No newline at end of file diff --git a/terraform/secrets-tool/logging.yaml b/terraform/secrets-tool/logging.yaml index 3aaf1619..3e31ffd4 100644 --- a/terraform/secrets-tool/logging.yaml +++ b/terraform/secrets-tool/logging.yaml @@ -49,6 +49,10 @@ loggers: level: DEBUG handlers: [console] propogate: yes + utils.terraform.wrapper: + level: DEBUG + handlers: [console] + propogate: yes commands: level: INFO handlers: [console] diff --git a/terraform/secrets-tool/utils/terraform/__init__.py b/terraform/secrets-tool/utils/terraform/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/terraform/secrets-tool/utils/terraform/wrapper.py b/terraform/secrets-tool/utils/terraform/wrapper.py new file mode 100644 index 00000000..96d44396 --- /dev/null +++ b/terraform/secrets-tool/utils/terraform/wrapper.py @@ -0,0 +1,61 @@ +import os +import logging +import subprocess + +logger = logging.getLogger(__name__) + +class TFWrapper: + """ + Command wrapper for terraform that injects secrets + from keyvault in to environment variables which + can then be used by terraform + """ + def __init__(self, keyvault: object): + self.keyvault = keyvault + self.env = '' + self.terraform_path = 'terraform' + + self._set_env() + + def _set_env(self): + # Prefix variables with TF_VAR_ + for secret in self.keyvault.list_secrets(): + name = 'TF_VAR_' + secret + val = self.keyvault.get_secret(secret) + os.environ[name] = val + # Set the environment with new vars + self.env = os.environ.copy() + return None + + def _run_tf(self, option: str): + try: + command = '{} {}'.format(self.terraform_path, option) + with subprocess.Popen(command, env=self.env, stdout=subprocess.PIPE, shell=True) as proc: + for line in proc.stdout: + logging.info(line.decode("utf-8")) + except Exception as e: + print(e) + + def plan(self): + """ + terraform plan + """ + self._run_tf(option='plan') + + def init(self): + """ + terraform init + """ + self._run_tf(option='init') + + def apply(self): + """ + terraform apply + """ + self._run_tf(option='apply -auto-approve') + + def destroy(self): + """ + terraform destroy + """ + self._run_tf(option='destroy') \ No newline at end of file