github.com/lymingtonprecision/terraform@v0.9.9-0.20170613092852-62acef9611a9/command/import.go (about)

     1  package command
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"os"
     7  	"strings"
     8  
     9  	"github.com/hashicorp/terraform/backend"
    10  	"github.com/hashicorp/terraform/config"
    11  	"github.com/hashicorp/terraform/config/module"
    12  	"github.com/hashicorp/terraform/terraform"
    13  )
    14  
    15  // ImportCommand is a cli.Command implementation that imports resources
    16  // into the Terraform state.
    17  type ImportCommand struct {
    18  	Meta
    19  }
    20  
    21  func (c *ImportCommand) Run(args []string) int {
    22  	// Get the pwd since its our default -config flag value
    23  	pwd, err := os.Getwd()
    24  	if err != nil {
    25  		c.Ui.Error(fmt.Sprintf("Error getting pwd: %s", err))
    26  		return 1
    27  	}
    28  
    29  	var configPath string
    30  	args = c.Meta.process(args, true)
    31  
    32  	cmdFlags := c.Meta.flagSet("import")
    33  	cmdFlags.IntVar(&c.Meta.parallelism, "parallelism", 0, "parallelism")
    34  	cmdFlags.StringVar(&c.Meta.statePath, "state", DefaultStateFilename, "path")
    35  	cmdFlags.StringVar(&c.Meta.stateOutPath, "state-out", "", "path")
    36  	cmdFlags.StringVar(&c.Meta.backupPath, "backup", "", "path")
    37  	cmdFlags.StringVar(&configPath, "config", pwd, "path")
    38  	cmdFlags.StringVar(&c.Meta.provider, "provider", "", "provider")
    39  	cmdFlags.BoolVar(&c.Meta.stateLock, "lock", true, "lock state")
    40  	cmdFlags.DurationVar(&c.Meta.stateLockTimeout, "lock-timeout", 0, "lock timeout")
    41  	cmdFlags.Usage = func() { c.Ui.Error(c.Help()) }
    42  	if err := cmdFlags.Parse(args); err != nil {
    43  		return 1
    44  	}
    45  
    46  	args = cmdFlags.Args()
    47  	if len(args) != 2 {
    48  		c.Ui.Error("The import command expects two arguments.")
    49  		cmdFlags.Usage()
    50  		return 1
    51  	}
    52  
    53  	// Validate the provided resource address for syntax
    54  	addr, err := terraform.ParseResourceAddress(args[0])
    55  	if err != nil {
    56  		c.Ui.Error(fmt.Sprintf(importCommandInvalidAddressFmt, err))
    57  		return 1
    58  	}
    59  	if !addr.HasResourceSpec() {
    60  		// module.foo target isn't allowed for import
    61  		c.Ui.Error(importCommandMissingResourceSpecMsg)
    62  		return 1
    63  	}
    64  	if addr.Mode != config.ManagedResourceMode {
    65  		// can't import to a data resource address
    66  		c.Ui.Error(importCommandResourceModeMsg)
    67  		return 1
    68  	}
    69  
    70  	// Load the module
    71  	var mod *module.Tree
    72  	if configPath != "" {
    73  		var err error
    74  		mod, err = c.Module(configPath)
    75  		if err != nil {
    76  			c.Ui.Error(fmt.Sprintf("Failed to load root config module: %s", err))
    77  			return 1
    78  		}
    79  	}
    80  
    81  	// Verify that the given address points to something that exists in config.
    82  	// This is to reduce the risk that a typo in the resource address will
    83  	// import something that Terraform will want to immediately destroy on
    84  	// the next plan, and generally acts as a reassurance of user intent.
    85  	targetMod := mod.Child(addr.Path)
    86  	if targetMod == nil {
    87  		modulePath := addr.WholeModuleAddress().String()
    88  		if modulePath == "" {
    89  			c.Ui.Error(importCommandMissingConfigMsg)
    90  		} else {
    91  			c.Ui.Error(fmt.Sprintf(importCommandMissingModuleFmt, modulePath))
    92  		}
    93  		return 1
    94  	}
    95  	rcs := targetMod.Config().Resources
    96  	var rc *config.Resource
    97  	for _, thisRc := range rcs {
    98  		if addr.MatchesConfig(targetMod, thisRc) {
    99  			rc = thisRc
   100  			break
   101  		}
   102  	}
   103  	if rc == nil {
   104  		modulePath := addr.WholeModuleAddress().String()
   105  		if modulePath == "" {
   106  			modulePath = "the root module"
   107  		}
   108  		c.Ui.Error(fmt.Sprintf(
   109  			importCommandMissingResourceFmt,
   110  			addr, modulePath, addr.Type, addr.Name,
   111  		))
   112  		return 1
   113  	}
   114  
   115  	// Load the backend
   116  	b, err := c.Backend(&BackendOpts{
   117  		Config: mod.Config(),
   118  	})
   119  	if err != nil {
   120  		c.Ui.Error(fmt.Sprintf("Failed to load backend: %s", err))
   121  		return 1
   122  	}
   123  
   124  	// We require a local backend
   125  	local, ok := b.(backend.Local)
   126  	if !ok {
   127  		c.Ui.Error(ErrUnsupportedLocalOp)
   128  		return 1
   129  	}
   130  
   131  	// Build the operation
   132  	opReq := c.Operation()
   133  	opReq.Module = mod
   134  
   135  	// Get the context
   136  	ctx, state, err := local.Context(opReq)
   137  	if err != nil {
   138  		c.Ui.Error(err.Error())
   139  		return 1
   140  	}
   141  
   142  	// Perform the import. Note that as you can see it is possible for this
   143  	// API to import more than one resource at once. For now, we only allow
   144  	// one while we stabilize this feature.
   145  	newState, err := ctx.Import(&terraform.ImportOpts{
   146  		Targets: []*terraform.ImportTarget{
   147  			&terraform.ImportTarget{
   148  				Addr:     args[0],
   149  				ID:       args[1],
   150  				Provider: c.Meta.provider,
   151  			},
   152  		},
   153  	})
   154  	if err != nil {
   155  		c.Ui.Error(fmt.Sprintf("Error importing: %s", err))
   156  		return 1
   157  	}
   158  
   159  	// Persist the final state
   160  	log.Printf("[INFO] Writing state output to: %s", c.Meta.StateOutPath())
   161  	if err := state.WriteState(newState); err != nil {
   162  		c.Ui.Error(fmt.Sprintf("Error writing state file: %s", err))
   163  		return 1
   164  	}
   165  	if err := state.PersistState(); err != nil {
   166  		c.Ui.Error(fmt.Sprintf("Error writing state file: %s", err))
   167  		return 1
   168  	}
   169  
   170  	c.Ui.Output(c.Colorize().Color("[reset][green]\n" + importCommandSuccessMsg))
   171  
   172  	return 0
   173  }
   174  
   175  func (c *ImportCommand) Help() string {
   176  	helpText := `
   177  Usage: terraform import [options] ADDR ID
   178  
   179    Import existing infrastructure into your Terraform state.
   180  
   181    This will find and import the specified resource into your Terraform
   182    state, allowing existing infrastructure to come under Terraform
   183    management without having to be initially created by Terraform.
   184  
   185    The ADDR specified is the address to import the resource to. Please
   186    see the documentation online for resource addresses. The ID is a
   187    resource-specific ID to identify that resource being imported. Please
   188    reference the documentation for the resource type you're importing to
   189    determine the ID syntax to use. It typically matches directly to the ID
   190    that the provider uses.
   191  
   192    In the current state of Terraform import, the resource is only imported
   193    into your state file. Once it is imported, you must manually write
   194    configuration for the new resource or Terraform will mark it for destruction.
   195    Future versions of Terraform will expand the functionality of Terraform
   196    import.
   197  
   198    This command will not modify your infrastructure, but it will make
   199    network requests to inspect parts of your infrastructure relevant to
   200    the resource being imported.
   201  
   202  Options:
   203  
   204    -backup=path        Path to backup the existing state file before
   205                        modifying. Defaults to the "-state-out" path with
   206                        ".backup" extension. Set to "-" to disable backup.
   207  
   208    -config=path        Path to a directory of Terraform configuration files
   209                        to use to configure the provider. Defaults to pwd.
   210                        If no config files are present, they must be provided
   211                        via the input prompts or env vars.
   212  
   213    -input=true         Ask for input for variables if not directly set.
   214  
   215    -lock=true          Lock the state file when locking is supported.
   216  
   217    -lock-timeout=0s    Duration to retry a state lock.
   218  
   219    -no-color           If specified, output won't contain any color.
   220  
   221    -provider=provider  Specific provider to use for import. This is used for
   222                        specifying aliases, such as "aws.eu". Defaults to the
   223                        normal provider prefix of the resource being imported.
   224  
   225    -state=path         Path to read and save state (unless state-out
   226                        is specified). Defaults to "terraform.tfstate".
   227  
   228    -state-out=path     Path to write updated state file. By default, the
   229                        "-state" path will be used.
   230  
   231    -var 'foo=bar'      Set a variable in the Terraform configuration. This
   232                        flag can be set multiple times. This is only useful
   233                        with the "-config" flag.
   234  
   235    -var-file=foo       Set variables in the Terraform configuration from
   236                        a file. If "terraform.tfvars" is present, it will be
   237                        automatically loaded if this flag is not specified.
   238  
   239  
   240  `
   241  	return strings.TrimSpace(helpText)
   242  }
   243  
   244  func (c *ImportCommand) Synopsis() string {
   245  	return "Import existing infrastructure into Terraform"
   246  }
   247  
   248  const importCommandInvalidAddressFmt = `Error: %s
   249  
   250  For information on valid syntax, see:
   251  https://www.terraform.io/docs/internals/resource-addressing.html
   252  `
   253  
   254  const importCommandMissingResourceSpecMsg = `Error: resource address must include a full resource spec
   255  
   256  For information on valid syntax, see:
   257  https://www.terraform.io/docs/internals/resource-addressing.html
   258  `
   259  
   260  const importCommandResourceModeMsg = `Error: resource address must refer to a managed resource.
   261  
   262  Data resources cannot be imported.
   263  `
   264  
   265  const importCommandMissingConfigMsg = `Error: no configuration files in this directory.
   266  
   267  "terraform import" can only be run in a Terraform configuration directory.
   268  Create one or more .tf files in this directory to import here.
   269  `
   270  
   271  const importCommandMissingModuleFmt = `Error: %s does not exist in the configuration.
   272  
   273  Please add the configuration for the module before importing resources into it.
   274  `
   275  
   276  const importCommandMissingResourceFmt = `Error: resource address %q does not exist in the configuration.
   277  
   278  Before importing this resource, please create its configuration in %s. For example:
   279  
   280  resource %q %q {
   281    # (resource arguments)
   282  }
   283  `
   284  
   285  const importCommandSuccessMsg = `Import successful!
   286  
   287  The resources that were imported are shown above. These resources are now in
   288  your Terraform state and will henceforth be managed by Terraform.
   289  
   290  Import does not generate configuration, so the next step is to ensure that
   291  the resource configurations match the current (or desired) state of the
   292  imported resources. You can use the output from "terraform plan" to verify that
   293  the configuration is correct and complete.
   294  `