diff --git a/command/plan.go b/command/plan.go index 0c2875363..8511c16e6 100644 --- a/command/plan.go +++ b/command/plan.go @@ -3,6 +3,7 @@ package command import ( "flag" "fmt" + "log" "os" "strings" @@ -20,10 +21,11 @@ type PlanCommand struct { func (c *PlanCommand) Run(args []string) int { var refresh bool - var statePath string + var outPath, statePath string cmdFlags := flag.NewFlagSet("plan", flag.ContinueOnError) cmdFlags.BoolVar(&refresh, "refresh", true, "refresh") + cmdFlags.StringVar(&outPath, "out", "", "path") cmdFlags.StringVar(&statePath, "state", "", "path") cmdFlags.Usage = func() { c.Ui.Error(c.Help()) } if err := cmdFlags.Parse(args); err != nil { @@ -85,8 +87,22 @@ func (c *PlanCommand) Run(args []string) int { if plan.Diff.Empty() { c.Ui.Output("No changes. Infrastructure is up-to-date.") - } else { - c.Ui.Output(strings.TrimSpace(plan.String())) + return 0 + } + + c.Ui.Output(strings.TrimSpace(plan.String())) + + if outPath != "" { + log.Printf("[INFO] Writing plan output to: %s", outPath) + f, err := os.Create(outPath) + if err == nil { + defer f.Close() + err = terraform.WritePlan(plan, f) + } + if err != nil { + c.Ui.Error(fmt.Sprintf("Error writing plan file: %s", err)) + return 1 + } } return 0 @@ -101,6 +117,9 @@ Usage: terraform plan [options] [terraform.tf] Options: + -out=path Write a plan file to the given path. This can be used as + input to the "apply" command. + -refresh=true Update state prior to checking for differences. -state=statefile Path to a Terraform state file to use to look diff --git a/command/plan_test.go b/command/plan_test.go index 06e416407..ff2f77dd0 100644 --- a/command/plan_test.go +++ b/command/plan_test.go @@ -39,6 +39,44 @@ func TestPlan_noState(t *testing.T) { } } +func TestPlan_outPath(t *testing.T) { + tf, err := ioutil.TempFile("", "tf") + if err != nil { + t.Fatalf("err: %s", err) + } + outPath := tf.Name() + os.Remove(tf.Name()) + + p := testProvider() + ui := new(cli.MockUi) + c := &PlanCommand{ + TFConfig: testTFConfig(p), + Ui: ui, + } + + p.DiffReturn = &terraform.ResourceDiff{ + Destroy: true, + } + + args := []string{ + "-out", outPath, + testFixturePath("plan"), + } + if code := c.Run(args); code != 0 { + t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + } + + f, err := os.Open(outPath) + if err != nil { + t.Fatalf("err: %s", err) + } + defer f.Close() + + if _, err := terraform.ReadPlan(f); err != nil { + t.Fatalf("err: %s", err) + } +} + func TestPlan_refresh(t *testing.T) { p := testProvider() ui := new(cli.MockUi)