From bf884173efbcb7c3c498de97db92f605901206fe Mon Sep 17 00:00:00 2001 From: Junfeng Wu Date: Mon, 9 Dec 2024 18:44:29 +0800 Subject: [PATCH] feat: add an option to pass sensitive variable through CLI arg (#15) --- pkg/terraform/terraform.go | 59 +++++++++++++++++++++++++++----------- 1 file changed, 43 insertions(+), 16 deletions(-) diff --git a/pkg/terraform/terraform.go b/pkg/terraform/terraform.go index 635a2fd..4301e09 100644 --- a/pkg/terraform/terraform.go +++ b/pkg/terraform/terraform.go @@ -72,8 +72,11 @@ type ModuleOptions struct { ModulePath string BackendConfigFileName string BackendConfig []byte - VariableFileName string - VariablePayload []byte + // A list of sensitive variable assignments. This will be passed to tfexec + // through `-var` arg. + SensitiveVariables map[string]string + VariableFileName string + VariablePayload []byte } type TFInitOptions struct { @@ -110,6 +113,7 @@ func (t *Terraform) ApplyModule(ctx context.Context, moduleOptions ModuleOptions ctx, absModulePath, backendCfgPath, + moduleOptions.SensitiveVariables, applyOptions, ) if err != nil { @@ -129,16 +133,16 @@ type DestroyOptions struct { StdErr io.Writer } -func (t *Terraform) DestroyModule(ctx context.Context, options ModuleOptions, destroyOptions DestroyOptions) error { - absModulePath := fmt.Sprintf("%s/%s", t.rootPath, options.ModulePath) - backendCfgPath := fmt.Sprintf("%s/%s", absModulePath, options.BackendConfigFileName) - err := os.WriteFile(backendCfgPath, []byte(options.BackendConfig), 0666) +func (t *Terraform) DestroyModule(ctx context.Context, moduleOptions ModuleOptions, destroyOptions DestroyOptions) error { + absModulePath := fmt.Sprintf("%s/%s", t.rootPath, moduleOptions.ModulePath) + backendCfgPath := fmt.Sprintf("%s/%s", absModulePath, moduleOptions.BackendConfigFileName) + err := os.WriteFile(backendCfgPath, []byte(moduleOptions.BackendConfig), 0666) if err != nil { return eris.Wrapf(err, "failed to write tf backend config to %v", backendCfgPath) } - variablePath := fmt.Sprintf("%s/%s", absModulePath, options.VariableFileName) - err = os.WriteFile(variablePath, []byte(options.VariablePayload), 0666) + variablePath := fmt.Sprintf("%s/%s", absModulePath, moduleOptions.VariableFileName) + err = os.WriteFile(variablePath, []byte(moduleOptions.VariablePayload), 0666) if err != nil { return eris.Wrapf(err, "failed to write tf variable payloads to %v", variablePath) } @@ -147,6 +151,7 @@ func (t *Terraform) DestroyModule(ctx context.Context, options ModuleOptions, de ctx, absModulePath, backendCfgPath, + moduleOptions.SensitiveVariables, destroyOptions, ) if err != nil { @@ -348,7 +353,7 @@ func (t *Terraform) getTerraformExec(workingDir string) (*tfexec.Terraform, erro return tf, nil } -func (t *Terraform) terraformInitAndApply(ctx context.Context, workingDir, backendPath string, options ApplyOptions) error { +func (t *Terraform) terraformInitAndApply(ctx context.Context, workingDir, backendPath string, sensitiveVariables map[string]string, options ApplyOptions) error { tf, err := t.getTerraformExec(workingDir) if err != nil { return eris.Wrap(err, "failed to create Terraform exec") @@ -362,11 +367,18 @@ func (t *Terraform) terraformInitAndApply(ctx context.Context, workingDir, backe return eris.Wrap(err, "failed to init terraform") } - apply := func(ctx context.Context) error { - applyErr := tf.Apply(ctx, tfexec.GracefulShutdown(tfexec.GracefulShutdownConfig{ + tfexecApplyOptions := []tfexec.ApplyOption{ + tfexec.GracefulShutdown(tfexec.GracefulShutdownConfig{ Enable: true, Period: options.GracefulShutdownPeriod, - })) + }), + } + for _, assignment := range toVariableAssignments(sensitiveVariables) { + tfexecApplyOptions = append(tfexecApplyOptions, tfexec.Var(assignment)) + } + + apply := func(ctx context.Context) error { + applyErr := tf.Apply(ctx, tfexecApplyOptions...) lockErrInfo, ok := extractStateLockedError(applyErr) if options.LockExpirationDuration == 0 || !ok || time.Since(lockErrInfo.Created) < options.LockExpirationDuration { return applyErr @@ -381,7 +393,7 @@ func (t *Terraform) terraformInitAndApply(ctx context.Context, workingDir, backe return nil } -func (t *Terraform) terraformInitAndDestroy(ctx context.Context, workingDir, backendPath string, options DestroyOptions) error { +func (t *Terraform) terraformInitAndDestroy(ctx context.Context, workingDir, backendPath string, sensitiveVariables map[string]string, options DestroyOptions) error { tf, err := t.getTerraformExec(workingDir) if err != nil { return eris.Wrap(err, "failed to create Terraform exec") @@ -395,11 +407,18 @@ func (t *Terraform) terraformInitAndDestroy(ctx context.Context, workingDir, bac return eris.Wrap(err, "failed to init terraform") } - destroy := func(ctx context.Context) error { - destroyErr := tf.Destroy(ctx, tfexec.GracefulShutdown(tfexec.GracefulShutdownConfig{ + tfexecDestroyOptions := []tfexec.DestroyOption{ + tfexec.GracefulShutdown(tfexec.GracefulShutdownConfig{ Enable: true, Period: options.GracefulShutdownPeriod, - })) + }), + } + for _, assignment := range toVariableAssignments(sensitiveVariables) { + tfexecDestroyOptions = append(tfexecDestroyOptions, tfexec.Var(assignment)) + } + + destroy := func(ctx context.Context) error { + destroyErr := tf.Destroy(ctx, tfexecDestroyOptions...) lockErrInfo, ok := extractStateLockedError(destroyErr) if options.LockExpirationDuration == 0 || !ok || time.Since(lockErrInfo.Created) < options.LockExpirationDuration { return destroyErr @@ -483,3 +502,11 @@ func extractStateLockedError(err error) (LockErrInfo, bool) { } return LockErrInfo{}, false } + +func toVariableAssignments(variables map[string]string) []string { + var assignments []string + for k, v := range variables { + assignments = append(assignments, fmt.Sprintf("%s=%s", k, v)) + } + return assignments +}