From 039d36bf91b0b22142bcd6f110529ac35aecd042 Mon Sep 17 00:00:00 2001 From: David Glasser Date: Mon, 20 Jun 2016 18:06:28 -0700 Subject: [PATCH] command: add "apply -auto-approve=false" flag A common reason to want to use `terraform plan` is to have a chance to review and confirm a plan before running it. If in fact that is the only reason you are running plan, this new `terraform apply -auto-approve=false` flag provides an easier alternative to P=$(mktemp -t plan) terraform refresh terraform plan -refresh=false -out=$P terraform apply $P rm $P The flag defaults to true for now, but in a future version of Terraform it will default to false. --- backend/backend.go | 7 +++-- backend/local/backend_apply.go | 38 ++++++++++++++++++++++- command/apply.go | 15 ++++++++- command/meta_backend.go | 1 + website/docs/commands/apply.html.markdown | 3 ++ 5 files changed, 59 insertions(+), 5 deletions(-) diff --git a/backend/backend.go b/backend/backend.go index a84d33d63..3708cf40a 100644 --- a/backend/backend.go +++ b/backend/backend.go @@ -121,9 +121,10 @@ type Operation struct { // The options below are more self-explanatory and affect the runtime // behavior of the operation. - Destroy bool - Targets []string - Variables map[string]interface{} + Destroy bool + Targets []string + Variables map[string]interface{} + AutoApprove bool // Input/output/control options. UIIn terraform.UIInput diff --git a/backend/local/backend_apply.go b/backend/local/backend_apply.go index 269095940..7ebee241e 100644 --- a/backend/local/backend_apply.go +++ b/backend/local/backend_apply.go @@ -12,6 +12,7 @@ import ( "github.com/hashicorp/go-multierror" "github.com/hashicorp/terraform/backend" "github.com/hashicorp/terraform/command/clistate" + "github.com/hashicorp/terraform/command/format" "github.com/hashicorp/terraform/config/module" "github.com/hashicorp/terraform/state" "github.com/hashicorp/terraform/terraform" @@ -89,10 +90,37 @@ func (b *Local) opApply( // Perform the plan log.Printf("[INFO] backend/local: apply calling Plan") - if _, err := tfCtx.Plan(); err != nil { + plan, err := tfCtx.Plan() + if err != nil { runningOp.Err = errwrap.Wrapf("Error running plan: {{err}}", err) return } + + trivialPlan := plan.Diff == nil || plan.Diff.Empty() + hasUI := op.UIOut != nil && op.UIIn != nil + if hasUI && !op.AutoApprove && !trivialPlan { + op.UIOut.Output(strings.TrimSpace(approvePlanHeader) + "\n") + op.UIOut.Output(format.Plan(&format.PlanOpts{ + Plan: plan, + Color: b.Colorize(), + ModuleDepth: -1, + })) + desc := "Terraform will apply the plan described above.\n" + + "Only 'yes' will be accepted to approve." + v, err := op.UIIn.Input(&terraform.InputOpts{ + Id: "approve", + Query: "Do you want to apply the plan above?", + Description: desc, + }) + if err != nil { + runningOp.Err = errwrap.Wrapf("Error asking for approval: {{err}}", err) + return + } + if v != "yes" { + runningOp.Err = errors.New("Apply cancelled.") + return + } + } } // Setup our hook for continuous state updates @@ -288,3 +316,11 @@ Terraform encountered an error attempting to save the state before canceling the current operation. Once the operation is complete another attempt will be made to save the final state. ` + +const approvePlanHeader = ` +The Terraform execution plan has been generated and is shown below. +Resources are shown in alphabetical order for quick scanning. Green resources +will be created (or destroyed and then created if an existing resource +exists), yellow resources are being changed in-place, and red resources +will be destroyed. Cyan entries are data sources to be read. +` diff --git a/command/apply.go b/command/apply.go index a4bbba7a6..2ea9fa481 100644 --- a/command/apply.go +++ b/command/apply.go @@ -29,7 +29,7 @@ type ApplyCommand struct { } func (c *ApplyCommand) Run(args []string) int { - var destroyForce, refresh bool + var destroyForce, refresh, autoApprove bool args = c.Meta.process(args, true) cmdName := "apply" @@ -42,6 +42,9 @@ func (c *ApplyCommand) Run(args []string) int { cmdFlags.BoolVar(&destroyForce, "force", false, "force") } cmdFlags.BoolVar(&refresh, "refresh", true, "refresh") + if !c.Destroy { + cmdFlags.BoolVar(&autoApprove, "auto-approve", true, "skip interactive approval of plan before applying") + } cmdFlags.IntVar( &c.Meta.parallelism, "parallelism", DefaultParallelism, "parallelism") cmdFlags.StringVar(&c.Meta.statePath, "state", "", "path") @@ -112,6 +115,11 @@ func (c *ApplyCommand) Run(args []string) int { if plan != nil { // Reset the config path for backend loading configPath = "" + + if !autoApprove { + c.Ui.Error("Cannot combine -auto-approve=false with a plan file.") + return 1 + } } // Load the module if we don't have one yet (not running from plan) @@ -195,6 +203,7 @@ func (c *ApplyCommand) Run(args []string) int { opReq.Plan = plan opReq.PlanRefresh = refresh opReq.Type = backend.OperationTypeApply + opReq.AutoApprove = autoApprove // Perform the operation ctx, ctxCancel := context.WithCancel(context.Background()) @@ -289,6 +298,10 @@ Options: -lock-timeout=0s Duration to retry a state lock. + -auto-approve=true Skip interactive approval of plan before applying. In a + future version of Terraform, this flag's default value + will change to false. + -input=true Ask for input for variables if not directly set. -no-color If specified, output won't contain any color. diff --git a/command/meta_backend.go b/command/meta_backend.go index 87d193aa4..74422e1dc 100644 --- a/command/meta_backend.go +++ b/command/meta_backend.go @@ -168,6 +168,7 @@ func (m *Meta) Operation() *backend.Operation { PlanOutBackend: m.backendState, Targets: m.targets, UIIn: m.UIInput(), + UIOut: m.Ui, Workspace: m.Workspace(), LockState: m.stateLock, StateLockTimeout: m.stateLockTimeout, diff --git a/website/docs/commands/apply.html.markdown b/website/docs/commands/apply.html.markdown index 55c26c7ad..085a1444c 100644 --- a/website/docs/commands/apply.html.markdown +++ b/website/docs/commands/apply.html.markdown @@ -37,6 +37,9 @@ The command-line flags are all optional. The list of available flags are: * `-input=true` - Ask for input for variables if not directly set. +* `-auto-approve=true` - Skip interactive approval of plan before applying. In a + future version of Terraform, this flag's default value will change to false. + * `-no-color` - Disables output with coloring. * `-parallelism=n` - Limit the number of concurrent operation as Terraform