github.com/vic3lord/terraform@v0.8.0-rc1.0.20170626102919-16c6dd2cb372/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 `