diff --git a/command/plan.go b/command/plan.go index c9a275ade67c..09ce4778a439 100644 --- a/command/plan.go +++ b/command/plan.go @@ -61,7 +61,7 @@ func (c *PlanCommand) Run(args []string) int { // This is going to keep track of shadow errors var shadowErr error - ctx, _, err := c.Context(contextOpts{ + ctx, planned, err := c.Context(contextOpts{ Destroy: destroy, Path: path, StatePath: c.Meta.statePath, @@ -71,6 +71,17 @@ func (c *PlanCommand) Run(args []string) int { c.Ui.Error(err.Error()) return 1 } + if planned { + c.Ui.Output(c.Colorize().Color( + "[reset][bold][yellow]" + + "The plan command received a saved plan file as input. This command\n" + + "will output the saved plan. This will not modify the already-existing\n" + + "plan. If you wish to generate a new plan, please pass in a configuration\n" + + "directory as an argument.\n\n")) + + // Disable refreshing no matter what since we only want to show the plan + refresh = false + } err = terraform.SetDebugInfo(DefaultDataDir) if err != nil { @@ -171,7 +182,7 @@ func (c *PlanCommand) Run(args []string) int { func (c *PlanCommand) Help() string { helpText := ` -Usage: terraform plan [options] [dir] +Usage: terraform plan [options] [DIR-OR-PLAN] Generates an execution plan for Terraform. @@ -180,6 +191,9 @@ Usage: terraform plan [options] [dir] a Terraform plan file, and apply can take this plan file to execute this plan exactly. + If a saved plan is passed as an argument, this command will output + the saved plan contents. It will not modify the given plan. + Options: -destroy If set, a plan will be generated to destroy all resources diff --git a/command/plan_test.go b/command/plan_test.go index c4af9c1418fe..8e9537e2f447 100644 --- a/command/plan_test.go +++ b/command/plan_test.go @@ -37,6 +37,33 @@ func TestPlan(t *testing.T) { } } +func TestPlan_plan(t *testing.T) { + tmp, cwd := testCwd(t) + defer testFixCwd(t, tmp, cwd) + + planPath := testPlanFile(t, &terraform.Plan{ + Module: testModule(t, "apply"), + }) + + p := testProvider() + ui := new(cli.MockUi) + c := &PlanCommand{ + Meta: Meta{ + ContextOpts: testCtxConfig(p), + Ui: ui, + }, + } + + args := []string{planPath} + if code := c.Run(args); code != 0 { + t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + } + + if p.RefreshCalled { + t.Fatal("refresh should not be called") + } +} + func TestPlan_destroy(t *testing.T) { originalState := &terraform.State{ Modules: []*terraform.ModuleState{ diff --git a/website/source/docs/commands/plan.html.markdown b/website/source/docs/commands/plan.html.markdown index 84732a5ccbcc..48d5374736c1 100644 --- a/website/source/docs/commands/plan.html.markdown +++ b/website/source/docs/commands/plan.html.markdown @@ -16,11 +16,16 @@ to `terraform apply` to ensure only the pre-planned actions are executed. ## Usage -Usage: `terraform plan [options] [dir]` +Usage: `terraform plan [options] [dir-or-plan]` By default, `plan` requires no flags and looks in the current directory for the configuration and state file to refresh. +If the command is given an existing saved plan as an argument, the +command will output the contents of the saved plan. In this scenario, +the `plan` command will not modify the given plan. This can be used to +inspect a planfile. + The command-line flags are all optional. The list of available flags are: * `-destroy` - If set, generates a plan to destroy all the known resources.